Jaimir G. blog

Experiencias de mi trabajo diario construyendo soluciones sobre plataformas Microsoft

January 2009 - Posts

Riesgos de la ley 1273 del 2009 para delitos informáticos.

Gracias a Juan Carlos Coronado.
Gracias a Juan Carlos Coronado por la ilistración

El 5 de enero del 2009 entró en vigencia la nueva ley colombiana contra delitos informáticos denominada "De la Protección de la información y de los datos".  Basada en leyes de otros países, el espíritu de la misma es convertir en delito todos los "atentados" contra la información y los sistemas informáticos dándole castigos ejemplares a nuestros "Hackers" criollos.  La verdad, el tema dejó de ser un chiste pues las penas van desde 48 a 96 meses de cárcel y en multas de 100 a 1000 salarios mínimos legales mensuales vigentes.

A primera vista la ley no solo parece justa sino necesaria, pues la principal actividad criminal informática en este país es clonar nuestras tarjetas bancarias con el fin de robarnos los fondos, por lo tanto merecen todo el peso de la ley.  Además le da que pensar a más de un estudiante desocupado de ingeniería de sistemas que quieren parecerse a "Hugh Jackman" en la película "Swordfish" y lo único que logran es crear más de una molestia.

Sin embargo, como toda ley de la nación, ésta incluye un mico que incrementa el riesgo de ejercer nuestra profesión:

"Artículo 269D: DAÑO INFORMÁTICO. El que, sin estar facultado para ello, destruya, dañe, borre, deteriore, altere o suprima datos informáticos, o un sistema de tratamiento de información o sus partes o componentes lógicos, incurrirá en pena de prisión de cuarenta y ocho (48) a noventa y seis (96) meses y en multa de 100 a 1000 salarios mínimos legales mensuales vigentes."

Podría asegurar que todos los ingenieros de sistemas que trabajamos en mesas de soporte, hemos cometido nuestros pecadillos: Borrar carpetas de usuarios, formatear discos sin tomar copias, eliminar información de bases de datos, actualizar versiones de programas que hacen incompatibles los datos actuales, etc.  Anteriormente, a parte del susto del momento y de vernos obligados recuperar la información, todo termina como una anécdota con la promesa de no volver a hacerlo, incluso después de los años nos da risa cada vez que nos acordamos.   A partir de la nueva ley, el propietario de la información nos puede denunciar (no demandar) cual delincuentes y si un juez lo considera, podríamos terminar en la cárcel.

Para hacerlo más interesante, según el artículo 269H, la pena se puede duplicar sí el hecho se da "Sobre redes o sistemas informáticos o de comunicaciones estatales u oficiales o del sector financiero, nacionales o extranjeros.", esto incluye a los hospitales públicos del estado.

La ley existe y es necesaria, pero ahora se incrementa el riesgo de ejecutar nuestro trabajo.  Muchas personas se preguntan: ¿por qué en otros países la hora ingeniero de soporte es tan costosa? o ¿por qué las empresas multinacionales de informática cobran tan alto sus servicios?   Una de las razones principales es porque estas compañías toman pólizas de seguro que mitigan estos riesgos, y esos costos incrementan el valor de sus servicios.   Creo que en Colombia debemos considerar seriamente seguir este ejemplo.

No queda por demás recomendarles que mantengan sus copias al día, prueben que las copias restauran y dejan sus sistemas en el estado esperado; antes de hacerle mantenimiento a una máquina tengan una copia actualizada, actualicen la ficha de inventario de cada computador; NUNCA realicen pruebas de software sobre servidores que están en producción, tengan siempre un ambiente de pruebas al menos con maquinas virtuales; y finalmente, no toquen ningún computador, así se sea de su mejor amigo, sin la autorización explícita del propietario (preferiblemente por escrito).

El texto completo de la ley lo pueden obtener en http://web.presidencia.gov.co/leyes/2009/enero/ley127305012009.pdf.

Error “Attempted to read or write protected memory. This is often an indication that other memory is corrupt” con Oracle.

Al ejecutar un procedimiento almacenado de Oracle desde una aplicación que utiliza ADO.NET, se genera el siguiente mensaje de error:


Attempted to read or write protected memory. This is often an indication that other memory is corrupt.

Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.

Exception Details: System.AccessViolationException: Attempted to read or write protected memory. This is often an indication that other memory is corrupt.

Source Error:

An unhandled exception was generated during the execution of the current web request. Information regarding the origin and location of the exception can be identified using the exception stack trace below. 

Stack Trace:

[AccessViolationException: Attempted to read or write protected memory. This is often an indication that other memory is corrupt.]

   Oracle.DataAccess.Client.OpsSql.ExecuteReader(IntPtr opsConCtx, IntPtr& opsErrCtx, IntPtr& opsSqlCtx, IntPtr& opsDacCtx, IntPtr& opsReaderErrCtx, IntPtr opsSubscrCtx, Int32& isSubscrRegistered, Int32 bchgNTFNExcludeRowidInfo, Int32 bQueryBasedNTFNRegistration, Int64& query_id, OpoSqlValCtx*& pOpoSqlValCtx, String pCommandText, OpoDacValCtx*& pOpoDacValCtx, IntPtr[] pOpoPrmValCtx, String[] ppOpoPrmRefCtx, OpoMetValCtx*& pOpoMetValCtx, Int32 NoOfParams) +0
   Oracle.DataAccess.Client.OracleCommand.ExecuteReader(Boolean requery, Boolean fillRequest, CommandBehavior behavior) +2789
   Oracle.DataAccess.Client.OracleCommand.ExecuteDbDataReader(CommandBehavior behavior) +42
   System.Data.Common.DbCommand.ExecuteReader(CommandBehavior behavior) +10


En internet se encuentran muchas razones validas, conflicto entre las versiones del cliente Oracle y el servidor, algunos problemas de configuración del ODP.NET y el Oracle Cliente (home, tsnames, register, etc..), que no permite seleccionar la versión correcta de las librerías cliente al momento de comunicarse con el servidor. Básicamente cualquier cosa que afecte el búfer de ejecución o la transferencia de datos entre cliente y servidor de Oracle puede generar esta excepción. Sin embargo, todavía existen muchos reportes en los foros donde se corrigen todo los puntos referentes al ambiente y la configuración de la plataforma y aun sigue presentándose el error.

Debo decir que antes de llegar a estas conclusiones y solucionar el problema, pase mucho tiempo probando diferentes soluciones y siguiendo recomendaciones hechas en muchos foros. Cuando nada de esto funciono, empezamos a comparar que diferenciaba este procedimiento en especial a los demás y de allí llegamos al acercamiento que el problema estaba en la declaración de los parámetros. Para esta investigación conté con el apoyo de Beatriz Sanchez, una compañera que domina PL de Oracle.

En mi caso se presentaban las siguientes características:

  1. El procedimiento almacenado tiene un parámetro marcado como "IN OUT" de tipo "DATE".
  2. Dentro del cuerpo del procedimiento almacenado se asigna un nuevo valor al parámetro.
  3. En el comando de ADO.NET, en la colección de "Parameters", este parámetro se adiciono como "DbType.DateTime".

Colocando cometarios para inhabilitar parte del código del procedimiento almacenado, llegamos a la conclusión que el error se generaba cuando se cambiaba el valor o se asignaba un nuevo valor al parámetro, como esta misma acción se realiza con parámetros de tipo "VARCHAR" y "NUMERIC" sin ningún error, nos concentramos en la declaración del tipo de dato del parámetro.

Es importante aclarar que cuando los parámetros en Oracle son definidos utilizando los tipos genéricos ADO.NET ("DbType") el ODP.NET realiza el respectivo mapeo a los tipos "OracleDbType". En el caso del "DbType.DateTime" es mapeado a "OracleDbType.TimeStamp" con el fin de conservar las milésimas de segundo, pues el tipo de dato en Oracle "DATE" solo almacena hasta segundos.

La primera aproximación fue editar la definición del procedimiento en Oracle y cambiar la declaración del parámetro al tipo de dato "TIMESTAMP", sin embargo el error se volvió a generar, lo cual nos lleva a la primera conclusión:

"Los parámetros de un procedimiento almacenado de tipo "TIMESTAMP" declarados como "IN OUT" para poder asignarles un nuevo valor, generan error "System.AccessViolationException" al ejecutarse desde ADO.NET."

Después cambiamos en la declaración del comando de ADO.NET y utilizando "OracleParameter" se asignó el tipo de dato "OracleDbType.Date". En este caso la aplicación corrió sin errores y permitió cambiar el valor del parámetro dentro del procedimiento. Lo cual nos lleva a otras dos conclusiones:

"El mapeo realizado por ODP.NET entre "DbType" y "OracleDbType" no es acertado en todos los escenarios y en varios casos es necesario crear un función propia que realice la equivalencia", esta es nuestra función.

private static OracleDbType EquivalenciasOracle(DbType dbType)
{
    switch (dbType)
    {
       case DbType.AnsiString: return OracleDbType.Varchar2;
       case DbType.AnsiStringFixedLength: return OracleDbType.Char;
       case DbType.Binary: return OracleDbType.Raw;
       case DbType.Boolean: return OracleDbType.Byte;
       case DbType.Byte: return OracleDbType.Byte;
       case DbType.Currency: return OracleDbType.Decimal;
       case DbType.Date: return OracleDbType.Date;
       case DbType.DateTime: return OracleDbType.Date;
       case DbType.Decimal: return OracleDbType.Decimal;
       case DbType.Double: return OracleDbType.Double;
       case DbType.Guid: return OracleDbType.Raw;
       case DbType.Int16: return OracleDbType.Int16;
       case DbType.Int32: return OracleDbType.Int32;
       case DbType.Int64: return OracleDbType.Int64;
       case DbType.Object: return OracleDbType.Blob;
       case DbType.SByte: return OracleDbType.Int16;
       case DbType.Single: return OracleDbType.Single;
       case DbType.String: return OracleDbType.NVarchar2;
       case DbType.StringFixedLength: return OracleDbType.NChar;
       case DbType.Time: return OracleDbType.TimeStamp;
       case DbType.UInt16: return OracleDbType.Int16;
       case DbType.UInt32: return OracleDbType.Int32;
       case DbType.UInt64: return OracleDbType.Int64;
       case DbType.VarNumeric: return OracleDbType.Decimal;
       default: return OracleDbType.Varchar2;
    }
}

Finalmente, "Es muy importante seleccionar el tipo de dato correcto en los parámetros, sobre todo si estos se van a utilizar como espacios de memoria donde se van a intercambiar valores entre Oracle y ADO.NET (IN OUT), pues NO existe un validación real en tiempo de desarrollo y esto puede generar problemas en tiempo de ejecución de asignación de memoria que derivan en errores de tipo "System.AccessViolationException""

Ambiente
Sistema Operativo Windows Vista Ultimate + Sp1
Microsoft .NET Framework 3.5
Microsoft Visual Studio 2008 Versión 9.0.21022.8 RTM
ODP.NET 11.1.0.6.20
Oracle 10g