¿Su código Java es seguro o está expuesto?
Resumen Si bien los clientes todavía están preocupados por la escalabilidad y la disponibilidad de las aplicaciones que usted crea para ellos, también pueden estar preocupados por la seguridad, y los requisitos pueden ser particularmente exigentes. Las aplicaciones pueden ser vulnerables a dos tipos de amenazas a la seguridad: amenazas estáticas y amenazas dinámicas. Si bien los desarrolladores no tienen control total sobre las amenazas dinámicas, existen algunas precauciones que se pueden tomar para eliminar las amenazas estáticas al desarrollar aplicaciones. Si bien los desarrolladores no tienen control total sobre las amenazas dinámicas, puedes tomar algunas precauciones para eliminar las amenazas estáticas al desarrollar tu aplicación. Este artículo resume y explica los tipos de exposiciones estáticas: son fallas en un sistema que lo exponen a atacantes que desean usurpar privilegios del sistema. Aprenderá cómo manejar estas exposiciones y cómo detectar los posibles efectos si no se manejan.
Al desarrollar una aplicación web Java, debe asegurarse de que la aplicación tenga la seguridad que necesita para garantizar que tu aplicación El programa tenga la seguridad que necesitas. Al desarrollar una aplicación web Java, debe asegurarse de que su aplicación tenga un conjunto completo de características de seguridad para complementarla. Cuando hablamos aquí de seguridad de Java, no nos referimos a las API de seguridad proporcionadas por el lenguaje Java, ni tampoco al uso de código Java para proteger sus aplicaciones. - Las características de seguridad de Java incluyen evitar que los atacantes usurpen privilegios del sistema, controlen las operaciones del sistema, comprometan los datos del sistema o asuman una confianza no autorizada.
No hay duda de que los clientes ahora están tan preocupados por el rendimiento, la escalabilidad y la disponibilidad como por la seguridad. No hay duda de que los clientes ahora son tan estrictos con respecto al rendimiento, la escalabilidad y la disponibilidad como lo son con respecto a la seguridad, y las aplicaciones pueden ser vulnerables a tipos de amenazas de seguridad tanto dinámicas como estáticas. Las amenazas dinámicas son amenazas relacionadas con el acceso no autorizado a los sistemas o con la integridad, privacidad y confidencialidad de los datos transmitidos a través de una red. Por el contrario, las amenazas estáticas están relacionadas con el código funcional de la aplicación y están relacionadas con las acciones de los usuarios autorizados que acceden al sistema. La entrada al sistema por parte de un usuario desconocido es un ejemplo de una amenaza dinámica. en el sistema de manera no autorizada o Los datos son un ejemplo de una amenaza estática. Los desarrolladores de aplicaciones no tienen control total sobre las amenazas dinámicas, pero pueden tomar precauciones al crear aplicaciones para eliminar las amenazas estáticas.
En este artículo, analizaremos técnicas para abordar diversos riesgos estáticos, explicaremos para cada riesgo las implicaciones de no abordar estos problemas de seguridad y también recomendaremos algunas pautas que debe seguir para desarrollar un desarrollo sólido y aplicaciones Java seguras que no se vean amenazadas por estos riesgos de seguridad estáticos.
Consejos para manejar exposiciones de alta gravedad
Sigue estos consejos para evitar exposiciones de seguridad estáticas de alta gravedad
Limita el acceso a las variables acceder y hacer que cada clase y método sea final a menos que haya una buena razón para no hacerlo. Hacer que una clase no se pueda clonar Hacer que una clase no sea serializable Hacer que una clase sea irreversiblemente serializable Evitar codificar datos confidenciales Encontrar código malicioso Restringir el acceso a las variables
Si declara una variable como pública, el código externo puede ser manipulado, provocando un riesgo de seguridad
Impacto
Si una variable de instancia se declara como pública, se puede acceder a la variable de instancia y manipularla directamente en la instancia de clase. Declarar variables de instancia como protegidas no necesariamente resuelve este problema.
El listado muestra código que utiliza variables públicas.
public por lo que está expuesto
Listado de código usando variables públicas
class Test { public int id; protected String name; Test(){ id = ; = hola mundo; } // código } clase pública MiClase { id de int público; nombre de cadena protegida = ; nombre de cadena int público ; ; Test(){ public id = ; nombre = hola mundo; } //código//código } clase pública MyClass extiende Prueba{ método vacío públicoIllegalSet(String nombre){ este nombre = nombre // esto no debería estar permitido } público static void main(String[] args){ Test obj = new Test(); obj id = ; // esto no debe permitirse MyClass mc = new MyClass(); /p>
Recomendación
En general, se deben utilizar métodos de valor en lugar de variables públicas. Pasar
Lista de códigos sin usar variables públicas
Prueba de clase { private int id; private String name(){ id = ; } public void setId(int id){ este id = id; } public void setName(nombre de cadena){ este nombre = nombre } public int getId(){ return id;
Demuestre seguridad al
Hacer que cada clase y método sean finales
Las clases y métodos que no permiten la extensión deben declararse finales, para evitar que el código fuera del sistema ampliar la clase y modificar su comportamiento
Impacto
El simple hecho de declarar una clase como no pública no previene los ataques. Declarar una clase como no pública no impide que un atacante extienda la clase, ya que la clase seguirá siendo accesible desde su propio paquete
Recomendación
A menos que haya una buena razón para no hacerlo. de lo contrario, cada clase y método deben ser definitivos. La escalabilidad es su enemigo cuando intenta brindar seguridad. La extensibilidad solo brinda a los atacantes más formas de causarle problemas
No confíe en el alcance del paquete
Las clases, los métodos y las variables tienen su propio alcance del paquete y se puede acceder a las variables dentro su propio paquete
Impacto
Si el paquete Java no está cerrado, el atacante puede introducir una nueva clase y usar la nueva clase para acceder al contenido que desea proteger.
Sin embargo, algunos paquetes (como java lang) están desactivados de forma predeterminada y algunas JVM le permiten desactivar sus propios paquetes. Sin embargo, es mejor asumir que el paquete no está cerrado.
Recomendación
Desde una perspectiva de ingeniería de software, el alcance del paquete es muy importante porque evita el acceso accidental o no intencionado al contenido que desea. esconderse, pero no confíe en él por seguridad.
Protegido para adaptarse a sus necesidades específicas
Hacer que las clases no se puedan clonar
La clonación permite copiar fácilmente instancias de una clase, sin pasar por el constructor
Impacto
Incluso si no tiene la intención de hacer que una clase sea clonable, una fuente externa aún puede definir subclases de su clase y hacer que la clase sea clonable. Una fuente externa aún puede definir una subclase de su clase y hacer que esa subclase implemente java lang Cloneable, lo que permite a un atacante crear una nueva instancia de su clase, copiando la imagen de memoria de un objeto existente para generar una nueva instancia. Si bien esta forma de generar nuevos objetos a veces es aceptable, la mayoría de las veces no lo es. Código clonable
Clase MyClass{ private int id; private String nombre; public MyClass(){ id=; public MyClass(int id String nombre){ this; id=id; this name=name; } public void display(){ System out println( Id = +id+ \n + Nombre= +name); Clonable { public static void main(String[] args){ Hacker hack=new Hacker(); try{ MyClass o=(MyClass )hack clone() o display() } catch(CloneNotSupportedException e){ e printStackTrace(); ; } }}}}
Recomendación
Para evitar que su clase sea clonada, agregue los métodos que se muestran en la lista a su clase.
Los manifiestos hacen que su código no se pueda clonar
public final Object clone() throws java lang CloneNotSupportedException{ throw new java lang CloneNotSupportedException() }
;p>
Si quieres que tu clase sea clonable y has considerado las consecuencias de esta elección, aún puedes proteger tu clase. Para hacer esto, defina un método de clonación final en su clase y hágalo dependiente de un método que no sea de clonación en alguna superclase.
Enumere una forma segura de hacer que su código sea clonable
public final Object clone() throws java lang CloneNotSupportedException { super clone()
;La presencia de un método clone() en una clase evita que un atacante redefina su método de clonación
Hacer que la clase no sea serializable
La serialización permite instancias de clase Los datos en el El código comprometido clona o copia la instancia y luego la serializa
Impacto
La serialización es preocupante porque permite que fuentes externas obtengan control del estado interno del objeto.
Un atacante tiene visibilidad total del estado interno del objeto, incluidos los campos que usted marca como privado. También permite a un atacante acceder al estado interno de cualquier objeto al que haga referencia.
Recomendación
Para evitar que los objetos de una clase se serialicen, defina el método writeObject() del manifiesto en la clase
Para evitar que el objeto se serialice, defina el método writeObject() del manifiesto en la clase. p>
La lista evita que los objetos se serialicen
private final void writeObject(ObjectOutputStream out) lanza java io NotSerializableException { throw new java io NotSerializableException( este objeto no se puede serializar); }
Al declarar final el método writeObject(), se puede evitar que los atacantes sobrecarguen el método.
Hacer que una clase no sea serializable
Al utilizar la deserialización, un atacante puede crear una instancia de una clase utilizando datos externos o un flujo de bytes
Impacto
La deserialización se puede realizar en una clase independientemente de si es serializable o no. Una fuente externa puede crear una secuencia de bytes y deserializarla en una instancia de la clase, lo que le pone en gran riesgo porque no tiene control sobre el estado del objeto deserializado. Utilice la deserialización como otro constructor público del objeto: un constructor sobre el que no tiene control
Recomendación
Para evitar la deserialización del objeto, debe definir readObject, debe definir readObject método () en el manifiesto de la clase
El manifiesto evita la deserialización de objetos
private final void readObject( ObjectInputStream in) throws java io NotSerializableException { throw new java io NotSerializableException( Este objeto no se puede deserializar); }
Declarar un método como final evita que los atacantes anulen el método
Evite codificar datos confidenciales
Es posible que se sienta tentado. para almacenar datos confidenciales, como claves de cifrado, en el código de su aplicación o biblioteca, lo que generalmente lo hace más conveniente para los desarrolladores.
Cualquier usuario que ejecute su código tiene acceso completo a los secretos almacenados de esta manera. No hay nada que impida que un programador malvado o una máquina virtual husmeen en su código y aprendan sus secretos.
Recomendación
Puedes almacenar secretos en tu código de manera que solo tú puedas descifrarlos. En este caso, el secreto sólo existe en el algoritmo utilizado por su código. No hay nada de malo en hacer esto, pero no se confíe porque brinda una protección sólida. Puede enmascarar su código fuente o código de bytes, es decir, cifrarlo de tal manera que se deba conocer el formato de cifrado para poder descifrarlo, pero lo más probable es que el atacante pueda deducir el formato de cifrado y no pueda hacerlo. este. Pero un atacante probablemente podría deducir el formato de cifrado y aplicar ingeniería inversa a la máscara, revelando sus secretos.
Una posible solución a este problema es almacenar datos confidenciales en archivos de propiedades para que puedan leerse siempre que se necesiten. Si los datos son extremadamente confidenciales, su aplicación debe utilizar algunas técnicas de cifrado/descifrado al acceder al archivo de propiedades. > Encuentre código malicioso
Los desarrolladores sin escrúpulos que trabajan en un proyecto pueden introducir deliberadamente código vulnerable para explotarlo más adelante. Dicho código puede inicializarse iniciando un proceso en segundo plano, abriendo así una puerta trasera para intrusos, y también puede cambiar datos confidenciales.
Hay tres tipos de código malicioso.
Tres tipos de código malicioso
Métodos principales en clases Métodos definidos y no utilizados Código muerto en comentarios
Impacto
Los programas de punto de entrada pueden ser peligrosos y maliciosos Normalmente, Java los desarrolladores tienden a escribir el método main() en una clase, lo que ayuda a probar la funcionalidad de una sola clase mientras la pasa de prueba a producción. Cuando las clases pasan de pruebas a producción, las clases con métodos main() se convierten en una amenaza potencial para la aplicación porque los intrusos las usarán como puntos de entrada.
Verifique su código en busca de métodos no utilizados. Estos métodos pasarán todos los controles de seguridad. durante las pruebas, ya que no se llamarán en código, pero pueden contener datos confidenciales codificados (aunque sean datos de prueba). Datos confidenciales (aunque sean datos de prueba) Un atacante que introduce un pequeño fragmento de código puede llamar a dichos métodos
Evitar el código inactivo en la aplicación final (código en los comentarios) Si un intruso elimina dichos comentarios del código, el código puede afectar la funcionalidad del sistema
Se pueden ver ejemplos de los tres tipos de código malicioso en la lista
Enumera códigos potencialmente maliciosos. > Listar código Java potencialmente malicioso
public void usedMethod(){ // El propósito de escribir código es dañar el sistema} public void usedMethod(){ // El propósito de escribir código es dañar el sistema} public void usedMethod(){ // El propósito de escribir código es dañar el sistema} usedMethod() // Si no se divide, las funciones del sistema pueden verse afectadas // int x =; // x=x+ ; // Si no se divide, la funcionalidad del sistema puede verse afectada.
Recomendación
Los métodos no utilizados y el código inactivo en el método Main() (que no sea el método main() que inicia la aplicación) deben eliminarse del código de la aplicación. Los desarrolladores líderes deben realizar revisiones exhaustivas del código de aplicaciones confidenciales. Se deben utilizar stubs o clases virtuales para probar la funcionalidad de la aplicación en lugar de utilizar el método main()
Consejos para manejar vulnerabilidades de seguridad de gravedad moderada
Siga estos consejos para evitar la gravedad moderada vulnerabilidades Vulnerabilidad de seguridad estática
No confíe en la inicialización.
No confíe en la inicialización. No compare clases por nombre. No utilice clases internas.
Puede asignar objetos sin ejecutar el constructor. no son seguros de usar porque no los inicializa el constructor
Impacto
Validar el objeto durante la inicialización garantiza los datos
Por ejemplo, imagina que se crea un objeto de cuenta una cuenta nueva para un cliente, sólo es posible realizar dicha validación en el constructor cuando el saldo inicial de la cuenta es mayor que
.
Recomendación
Verificar la inicialización del objeto antes de usarlo. Para hacer esto, cada clase debe establecer un indicador booleano privado en el constructor, como se muestra en la lista de clases. que en cada método no estático, el código debe verificar el valor de este indicador antes de continuar con la ejecución. Si el valor del indicador es verdadero, el control debe continuar la ejecución; de lo contrario, el control debe generar una excepción y detener la ejecución. El método llamado desde el constructor no verifica las variables de inicialización porque la bandera no está configurada cuando se llama al método. Dado que estos métodos no verifican las etiquetas, deben declararse privados para evitar que los usuarios accedan directamente a estos métodos.
Los listados usan indicadores booleanos para verificar el proceso de inicialización
public class MyClass{ private boolean inicializado = false; //Otras variables public MyClass (){ / /método de inicialización de variable (); inicializado = verdadero; } método privado void (){ //no es necesario verificar la variable de inicialización //código } método público void (){ prueba { if(inicializado==verdadero){ //continuar con la lógica de negocios } else{ throw new Exception(estado ilegal del objeto); }}}catch(Exception e){ e printStackTrace(); }}}
Si el objeto se inicializa mediante deserialización, Entonces, el mecanismo de validación discutido anteriormente no funcionará ya que no se llama a ningún constructor durante este proceso. En este caso, la clase debería implementar la interfaz ObjectInputValidation.
Lista de implementación ObjectInputValidation
interfaz java io ObjectInputValidation { public void validarObject() throws InvalidObjectException }
Todas las validaciones deben estar en; Ejecutado en el método validarObject(). El objeto también debe llamar al método ObjectInputStream RegisterValidation() para registrarse para la validación después de deserializar el objeto. El primer parámetro del método RegisterValidation() es un objeto que implementa validarObject(), normalmente una referencia al propio objeto. El segundo parámetro de RegisterValidation() es una prioridad entera, que determina el orden de las devoluciones de llamada. Aquellos con prioridades más altas se llaman primero que aquellos con prioridades más bajas. El orden de las devoluciones de llamada dentro de la misma prioridad no está definido.
Cuando el objeto se deserializa, ObjectInputStream llamará a validarObject() en cada objeto registrado en orden de mayor a menor prioridad.
La validación de objeto() es una devolución de llamada que valida el nombre del objeto, pero no es una validación del objeto.
No compares clases por nombre
A veces es posible que necesites comparar las clases de dos objetos para ver si son iguales, o quizás quieras ver si un objeto es un Instancias específicas de clases, debido a que la JVM puede contener múltiples clases con el mismo nombre (clases con el mismo nombre en diferentes paquetes), por lo que no debe comparar clases por nombre. Comparar clases por nombre
Impacto
Si compara clases por nombre, puede otorgar sin darse cuenta permisos a la clase de un intruso que no desea otorgar a otros, porque el intruso puede Defina una clase con el mismo nombre que su clase.
Por ejemplo, supongamos que desea determinar si un objeto es una instancia de la barra de clases Foo.
Forma incorrecta de realizar esta tarea
Forma incorrecta de enumerar clases de comparación
if(obj getClass() getName()quals( Foo ))// Incorrecta ! // El nombre de la clase del objeto es Foo } else{ // La clase del objeto tiene otro nombre }
Recomendación
Debes tener mucho cuidado en situaciones en las que debes comparar clases por nombre. Debe asegurarse de que la clase actual esté utilizando el espacio de nombres actual del ClassLoader, como se muestra en la lista
If(obj getClass() getName()quals( Foo )) // ¡Incorrecto!// ¡Incorrecto p>
Mejor manera de enumerar clases de comparación
if(obj getClass() == this getClassLoader() loadClass( bar Foo )){ // object s la clase es igual a // la clase que esta clase llama bar Foo.la clase que esta clase llama bar Foo }else{ // la clase del objeto no es igual a la clase que // esta clase llama bar Foo }
Sin embargo, una mejor manera de comparar clases es comparar objetos de clase directamente para ver si son iguales. Por ejemplo, si desea determinar si dos objetos a y b pertenecen a la misma clase, debe usar el código de la lista
La lista compara directamente los objetos para ver si son iguales p>
if(a getClass() == b getClass()){ // Los objetos tienen la misma clase. Los objetos tienen la misma clase}else{ // Los objetos tienen clases diferentes}
Utilice comparaciones directas de nombres con moderación
No utilice clases internas
El código Java Bytes tiene no hay concepto de clases internas, porque el compilador convierte las clases internas en clases regulares. Si la clase interna no se declara privada, cualquier código en el mismo paquete puede acceder a la clase regular
Impact
Recomendación
No utilices clases internas si puedes prescindir de ellas
Consejos para manejar exposiciones de baja gravedad
Seguir Estos consejos ayudan a evitar exposiciones de seguridad estáticas de baja gravedad. Gravedad Riesgo de seguridad estático
Evite devolver valores estáticos seguros al devolver p>
Evite devolver objetos mutables Comprobar métodos nativos Evite devolver objetos mutables
Java El método devuelve una copia de la referencia del objeto. Si el objeto real es mutable, llamar a un programa con dicha referencia puede cambiar su contenido, lo cual generalmente no es deseable
Impacto
Mire esto. ejemplo. Por favor mire este ejemplo. Un método devuelve una referencia a una matriz interna de objetos confidenciales. Suponiendo que quien llama al método no cambia estos objetos, incluso si el objeto de matriz en sí es inmutable, es posible manipular el contenido de la matriz fuera del objeto de matriz, y dichas manipulaciones se reflejarán en los objetos devueltos al formación.
La manifestación devuelve una copia de referencia del objeto mutable
Este manifiesto demuestra una vulnerabilidad en el método getExposedObj(), que devuelve una copia de referencia del objeto expuesto. objeto, mientras que los objetos expuestos son mutables
La manifestación devuelve una copia de referencia del objeto mutable
class Exposed{ private int id; class Exposed{ private int; id clase Expuesto {privado int id; nombre de cadena privada; público expuesto(){} público expuesto (int id nombre de cadena){ este id = id este nombre = nombre} public int getId(){ return id; getName(){ nombre de retorno; } setId público void(int id){ este id=id; } setName público void(nombre de cadena){ este nombre = nombre } display vacío público(){ Sistema SQL Server para... } public void display(){ Sistema fuera println( id = + id + Nombre = + nombre); } }} public class Exp { privado ExposedObj = new Exposed( Harry Porter public Exposedobj(){ return expuestoObj; /devuelve una referencia al objeto } public static void main(String[] args){ Exp exp = new Exp (); exp getExposedObj() display(); Exposed expuesto = exp getExposedObj() expuesto setId( ); (Hacker); exp getExposedObj() mostrar();