Hola,
quiero presentarles el siguiente escenario:
En una computadora hay un servidor y en otra un cliente. Con el cliente queria hackear la contrasena del servidor. Si el cliente sabe la contrasena escribe en el servidor /login <contrasena>.
Codigo del Servidor:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Servidor
{
class Program
{
static void Main(string[] args)
{
Servidor_Chat chat = new Servidor_Chat();
}
}
}
Clase Servidor_Chat del servidor:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
using System.Net.Sockets;
using System.IO;
using System.Net;
namespace Servidor
{
class Servidor_Chat
{
/*
TcpListener--------> Espera la conexion del Cliente.
TcpClient----------> Proporciona la Conexion entre el Servidor y el Cliente.
NetworkStream------> Se encarga de enviar mensajes atravez de los sockets.
*/
private TcpListener server;
private TcpClient client = new TcpClient();
private IPEndPoint ipendpoint = new IPEndPoint(IPAddress.Any, 8000);
private List<Connection> list = new List<Connection>();
private string contrasena;
Connection con;
private struct Connection
{
public NetworkStream stream;
public StreamWriter streamw;
public StreamReader streamr;
public string nick;
}
public Servidor_Chat()
{
Inicio();
}
public void Inicio()
{
foreach (string line in File.ReadLines(@"Password.txt", Encoding.UTF8))
{
contrasena = line.Substring(12);
}
Console.WriteLine("Servidor OK!");
server = new TcpListener(ipendpoint);
server.Start();
while (true)
{
client = server.AcceptTcpClient();
con = new Connection();
con.stream = client.GetStream();
con.streamr = new StreamReader(con.stream);
con.streamw = new StreamWriter(con.stream);
con.nick = con.streamr.ReadLine();
list.Add(con);
Console.WriteLine(con.nick + " se a conectado.");
Thread t = new Thread(Escuchar_conexion);
t.Start();
}
}
void Escuchar_conexion()
{
Connection hcon = con;
do
{
try
{
string tmp = hcon.streamr.ReadLine();
Console.WriteLine(hcon.nick + ": " + tmp);
foreach (Connection c in list)
{
try
{
if(tmp == "/login " + contrasena)
{
c.streamw.WriteLine(hcon.nick + ", ahora eres administrador!");
c.streamw.Flush();
}
else
{
c.streamw.WriteLine(hcon.nick + ": " + tmp);
c.streamw.Flush();
}
}
catch
{
}
}
}
catch
{
list.Remove(hcon);
Console.WriteLine(con.nick + " se a desconectado.");
break;
}
} while (true);
}
}
}
Bueno, si es necesario poner el codigo del cliente, avisenme.
No se si lo puedo llamar inyeccion de codigo pero lo hare hasta que ustedes me digan que es incorrecto llamarlo asi a esto. Bueno trate de hacer una inyeccion de codigo poniendo en el texto de enviar del cliente: /login "" + contrasena + "" y despues, al ver que no funciono, envie: /login " + contrasena ".
Yo crei que el servidor, al recibir un texto, tiene lo recibido entre comillas. Por eso trate de cerrar las comillas (") luego de poner variable que contiene la contrasena (+ contrasena +) y luego de abrir otra comilla mas para cerrar la supuesta comilla final.
Si ustedes no me entienden debido a que me expreso mal quiero saber solo una cosa entonces.
Puedo hackear la contrasena desde la computadora cliente o tengo que hackear la computadora en donde esta el servidor para obtener la contrasena?
Esto lo estaba testeando en mi computadora.
Gracias y saludos
no es una inyección de codigo, es simplemente envío de datos D:
tmp == "/login " + contrasena
es decir si la contraseña es 1234 el login espera
/login 1234
/login "" + contrasena + ""
no se nada de c# pero ese codigo debió dar error, si te fijas bien no estás poniendo comillas dentro de comillas... estás abriendo y cerrando comillas justo inmediatamente es decir, un string vacío
para que se entienda mejor
if(vacio == "") console.log("es vacio")
por otro lado
/login " + contrasena ".
se está enviando (no se si interpreta la variable, lo dudo)
/login + contrasena
es decir, el servidor está recibiendo exactamente eso escrito arriba
tienes que usar tal cual está en el servidor
prueba = "/login " + contrasena
CitarPuedo hackear la contrasena desde la computadora cliente o tengo que hackear la computadora en donde esta el servidor para obtener la contrasena?
desde donde puedas enviar la contraseña al servidor
Cita de: TickTack en 23 Julio 2017, 20:41 PMNo se si lo puedo llamar inyeccion de codigo pero lo hare hasta que ustedes me digan que es incorrecto llamarlo asi a esto.
Lo que estás haciendo se denomina
IPC (Inter-Process Communication). Inyección de código... no lo es xD.
Si te lias con las comillas y con el operador de concatenación de
C#, recuerda que siempre tienes a tu disposición una herramienta muy útil de formato... precisamente para evitar la escritura excesiva (y horriblemente ininteligible cuando se usa en exceso) de comillas y de +++++. Me estoy refiriendo a la función
String.Fromat():
string str = string.Format("/login {0}", this.contrasena)
Saludos
Hola engel lex,
que bueno verte en la sección .NET de Programacion en General. Das muy buenos aportes siempre,
al igual que Elektro.
Citar
no se nada de c# pero ese codigo debió dar error, si te fijas bien no estás poniendo comillas
dentro de comillas...
En serio... o... que error.. o.. porque no me da error a mi. Solamente que no logro logearme
con esa contrasena sin poner esa contrasena....
Dices que lo tengo que enviar asi:?
prueba = "/login " + contrasena
Pero no funciona.
O dices que lo tenga que enviar asi:?
/login + contrasena
Asi tampoco funciona porque el servidor toma + contrasena como un string y no como una
variable. La idea era enviar /login + un envio de datos que permitiese al servidor
interpretar contrasena no como un string sino como una variable.
Gracias y saludos
Hola Elektro,
muchas gracias por la recomendacion.
Veo que me interprete mal. Ok miren:
Descarganse el servidor: http://www.mediafire.com/file/z7n27mai7p3upfo/Servidor.exe
Descarganse este documento de texto (ponganlo en donde este el servidor):
http://www.mediafire.com/file/jhqxcdupl40mnqe/Password.txt
Y descarganse el cliente: http://www.mediafire.com/file/o4lden8adjzd4ja/Cliente.exe
Ahora abran el servidor. Y luego el cliente. Ponen un nombre y denle a conectar.
Para loguearse escriban /login <contrasena>.
Lo que yo queria es loguearme sin saber la contrasena. Osea queria de una forma
escribir /login contrasena o algo parecido para que el servidor intreprete contrasena no
como un string sino como una variable para lograr loguearme como administrador sin poner
la contrasena y sin cambiar el codigo fuente del servidor.
Gracias y saludos
algunos consejos:
No suele ser muy útil, que el administrador se loguee de la misma forma que un cliente cualquiera.
De entrada si, hay una base de datos (clientes registrados), además de la contraseña, debería requerirse también el alias del usuario. Y por supuesto el admin, no debe llamarse 'admin', por mucho que en los routers venga así de mal...
Tampoco es buena idea registrar a cualquier cliente con una única contraseña.
Añade un método de 'AddCliente', donde el usuario especifique 3 cosas:
- Alias de registro. (login)
- Contraseña de registro. (login)
- Nombre de interfaz. (interfaz)
Rechaza el registro si:
- Alias o nombre tiene menos de 'x' caracteres. Usa una constantes c_MinCharsAlias = 6 (por ejemplo)
- Contraseña tiene menos de 'y' caracteres. ídem sobre la constante
- Alias y nombre son idénticos.
- Alias, nombre o contraseña, contienen caracteres que al caso considerares 'ilegales'... Esto es, debe tener un string con los caracteres legales "LegalChars = "ABCDEFGHIJK... abcdrfghijkl...01234...", una función debe chequear si algún carácter en un string recibido contiene un carácter que no aparece en LegalChars en cuyo caso devolver FALSE...
- Alias y/o nombre ya existen.
- Al patrón de la contraseña es fácilmente reconocible: por ejemplo "qwerty" "QWERTY", "123456", "PASSWORD", password"... Esto puede ser máso menos complejo, pero al menos hazte con una lista de las 100 contraseñas más frecuentes, las metes en un array y si coinciden lo recghazas... es lo mínimo. algo más potente es contabilizar los caracteres: mayusculas, minúsculas, números, espaciadores, simbolos. Exige que al menos haya caracteres en 3 de esos grupos, y que excepto el grupo con más caracteres (de la contraseña), sumen al menos 3 caracteres...
ejemplos:
---------- "ahsdfjktovuirmncv3" solo está el grupo minúsculas y en números solo contiene 1, rechazar no hay caracteres para 3 grupos al menos.
--------- "1845763W938564n" hay 3 grupos, pero aparte del más numeroso, solo hay 2 caracteres del resto, rechazar...
En fin puede complicarse más, pero algo similar, debe ser lo mínimo exigible.
El administrador, podría ser el único capacitado para encender el servidor. Esto exige que sea el primero en loguearse. Luego el servidor debe rechazar un login 'efectivo', si hay 0 usuarios logueados y éste no es administrador. al hacerlo así, implica que va a ser imposible que alguien se loguee como el administrador, toda vez que el servidor solo atiende cuando el administrador enciende el servidor. esta solución no siempre va a poder ser adecuada, especialmente cuando el administrador está en una máquina y el servidor reside en otra. El administrador podría ser automático, esto es cuando un cliente intenta loguearse, si hay 0 usuarios logueados, primero loguear internamente al administrador y luego al cliente. Y el administrador (remoto), cuando se loguee como un usuario cualquiera, el servidor debería poder reconocerle y reasignarle el administrador 'precargado'. Así ese administrador aparecerá como si fuera duplicado, esto es consta como un cliente normal y como administrador, pero tiene funciones y roles de administradro, ya que el servidor lo ha reconocido como tal.
Si todo es solo un proyecto de pruebas, tampoco hace falta que te compliques tanto con la seguridad, aunque algo mínimo debes hacer y tenerlo siempre presente la importancia de la seguridad.
Nunca almacenes las credenciales crudas, es decir si el alias de registro es: Tick-Tack y la contraseña: "pSd8F4 0a17Zh4", genera el hash de ambos y concaténalos y asocia un ID al azar y crea un hash también para ese ID (para crerar este hash toma el ID y le concatenas algún caracter del hash anterior, delante, detrás..), ... esos 3 campos es lo que debes guardar en tu BD. Así un usuario que se loguea, sigue el mismo patrón, se hashea su alias y contraseña y se concatenan, luego se busca si ese hash existe en la base de datos (debe devolver el ID si se encontró), si es así se da por válido el login, si no, que devuelva como ID = -1.
Si fue validado, al cliente entrégale un hash del ID (3º campo guardado). En lo sucesivo (mientras el user esté logueado), será esa referencia la identificación del usuario. Yendo un poco más lejos, este hash entregado debería crearse nuevo cada vez que un usuario se loguea, así no hay posibilidad de que si alguien tomo tu 'referencia' pueda usarla en un futuro...
Cada usuario logueado, añade su referencia a una tabla, así cualquier petición de un usuario antes de ser atendida, se verifica que existe en esa tabla...
En fin la seguridad dependerá mucho de como hagas las cosas.
Muchas gracias NEBIRE por tus puntos importantes para tomar medidas de seguridad a lo hora de crear o disenar un servidor de chat. Estaria bastante tiempo en todo lo que mencionaste...
Pero.. me intereso eso de crear un hash y concatenar sus datos.... Asi que... manos a la obra!!!
Pero antes...
Viste el servidor y cliente que subi a internet?
Antes queria saber si el cliente puede loguearse como administrador sin saber la contrasena.
Mas arriba postee el codigo del servidor.
Existe un vulnerabilidad de poder loguearse como administrador sin saber la contrasena en el servidor que hice?
Gracias y saludos
Los he descargado, pero no se ejecutan aquí tengo win-XP (32 bits).
---------------------
Respecto del login... fíjate que recurres a usar una contraseña que yace en un fichero de texto. Así, que en teoría eso lo hace altamente vulnerable. Además, ya te he comentado, que usar una sola y misma contraseña para todos es una malña solución.
--------------------
Si tu chat solo va a admitir un administrador, haz que sea el primero que se loguee, es decir él y solo él prende el servidor, y nadie más pueda loguearse como administrador. Si el administrador no se loguea, no existe el chat, porque no se crea.
Pero para poder loguear como administrador... en realidad para poder manejar diferentes usuarios con permisos diferentes, hay que separar los roles de usuario. Cuando te puse tiempo atrás un pseudocódigo sobre un chat, realmente allí te puse cosas muy útiles, bien que luego no hayas sabido aplicarlas. Los roles se pueden resumir en sus permisos-privilegios, y se puede reducir a su mínima expresión en una propiedad de tipo string (de solo lectura como cliente, lectura y escritura para los administradores).
Entonces para un usuario, lo mínimo como Rol, es una propiedad de solo lectura con ese nombre Rol y cuyo contenido sea una cadena de texto. Unos simples caracteres, así un ejemplo de los roles de un usuario podría ser algo tan sencillo como esto: cliente.Rol = "LSER", cuyo significado simple podría ser:puede Loguearse (ideal para retirarlo cuando se banea a un usuario), puede crear Salas, puede Enviar mensajes, puede Recibir mensajes.
En cambio el rol de Admin podrá usar muchos más métodos, luego su Rol, tendría un string, mucho más 'largo': Admin.Rol = "LSERXBPVG..."
X = puede eliminar user
B = puede Banear un usuario (al hacerlo se le retira a ese usaurio de su rol, la "L")
P = puede otorgar privilegios de administrador (admin restringido) a usuarios normales y retirar privilegios de administrador...
V = puede reiniciar el serVidor (pero no encender ni apagar)
G = Administrador Global. Ningún usuario puede tener este atributo, excepto el dueño del chat. Es decir el resto de administradores, habrá cosas que no pueden hacer. Solo quien tenga el privilegio G, podría apagar, encender el servidor
... = etc, etc... es cuestión de ver que quieres que haga.
Si un usuario (cliente) invoca un método, se verifica si su rol contiene el 'carácter' asociado a ese método, si es que sí, se permite la ejecución del método, si no, no. algunos métodos, en cambio los valida el servidor...
Al crearse un usuario se le añaden un rol básico... al crear el administrador su rol es completo.
Te pego el pseudocódigo de la clase servidor que te puse la otra vez (algo muy resumido, pero que te puede servir de orientación)... (este foro no admite spoilers, así que los mensajes a veces pueden quedar largos).
Date cuenta, que en este código, aparece Privilegios, como sinónimo de Rol, y por ejemplo: el privilegio "CreateSala" (que lo puse así por claridad), puede ser "S" (como aparece en el ejemplo más arriba), es decir resumido a un sólo carácter...
Clase Servidor
// Instancias de clases.
TablaHash Clientes Privado ConEventos
TablaHash ClientesOcultos Privado ConEventos // clientes privilegiados, admins
TablaHash Salas Privado ConEventos
Sala SalaRaiz Privado ConEventos
Cliente ClienteRaiz Privado ConEventos
Buleano Existe
Entero = Propiedad Sololectura NumClientes Publico
Numclientes = Clientes.Cantidad
Fin Propiedad
Entero = Propiedad SoloLectura NumclientesOcultos Publico // o admins
NumClientesOcultos = ClientesOcultos.Cantidad
Fin Propiedad
Entero = Propiedad SoloLectura NumSalas Publico
Numsalas = Salas.Cantidad
Fin Propiedad
Buleano = Propiedad SePermiteCrearSalas Oculto //lectura y escritura
// Esta función se invoca para crear el servidor, que luego es público y permite la conexión... crea además, la primera sala y el resto de objetos necesarios.
Funcion Nuevo(x,y,z) Oculta ' parámetros que requiriese para crear el servidor
Clientes = Nueva TablaHash
ClientesOcultos = Nueva TablaHash
Salas = NuevaTablaHash
SalaRaiz= Nueva Sala
ClienteRaiz = Nuevo Cliente(Alias, Invisible, Privilegios)
Salas.Add(SalaRaiz)
Clientes.Add(ClienteRaiz)
ClientesOcultos.Add(ClienteRaiz)
Existe = TRUE
Fin Funcion
// El ciente cuando se conecta al servidor, se añade a la lista de clientes.
// Y también se le añade a la salaRaiz, y se le devuelve una referencia a esa sala.
Sala = Funcion Conectar(Cliente Cli) Publico
Si SalaRaiz.AdmiteClientes = TRUE luego
//podría filtrarse aquí comparando con una lista de clientes/IPs, baneadas, pero quizás no sea muy efectiva...
Si SalaRaiz.Add(cli) = TRUE // solo debería devolver false si ya existe un cliente con el mismo alias.
Devolver SalaRaiz
Fin si
Fin si
Fin Funcion
Sala = Funcion CrearSala(Cliente Cli, String NombreSala, Entero MasUsers)
Si (SePermiteCrearSalas = TRUE) luego
Si (Clientes.Existe(cli) = TRUE ) luego // Todos los clientes, incluso los ocultos, constan en la colección Clientes.
Si (Salas.Existe(NombreSala) = FALSE) luego
Sala = Nueva Sala(Cli, Nombresala, MaxUsers, This)
Salas.Add(sala) //allí se dispara un evento de 'SalaCreada(Sala.nombre)' que le llega a todos los clientes.
Devolver Sala
Fin Si
Fin si
Fin si
// Ó si solo se permite a clientes ocultos (admins).
Si (ClientesOcultos.Existe(cli) = TRUE ) luego ...
....
// Ó si solo se permite a cliente con privilegios
Si (cli.Privilegios.Contiene("CreateSala")) luego
Si (Clientes.Existe(cli) = TRUE ) luego ...
....
Fin si
Fin Funcion
// Otras funciones que tenga el servidor... (especialmente resolver los eventos de las intancias de las clases: Clientes, ClientesOcultos, Salas, SalaRaiz, ClienteRaiz
Fin Clase
Fíjate comno en este ejemplo, la instancia del servidor cuando se crea, es creada por el administrador cuando invoca el New(x,y,z) x,y,z representa los parámetros que vayan a ser necesarios durante la creación de la instancia.. fíjate que luego en ese mismo métodos se concreta:
ClienteRaiz = Nuevo Cliente(Alias, Invisible, Privilegios)
...Alias, invisible y privilegios, serían parámetros de entrada (al margen de otros parámetros más fueren precisos). Y al caso, el admin, ni siquiera precisa una contraseña. Ya que es él quien crea la instancia, es el poseedor de la misma, el método New, es el constructor de la instancia, solo puede ser invocado una única vez y además al inicio antes que nada suceda, luego nadie puede usurpar el administrador-absoluto (si no hay métodos para un login posterior de administrador, por ejemplo), a lo sumo podrán crear otras instancias del servidor (...si lograran acceso al terminal que lo contiene)...
Digo administrador absoluto, porque en un chat, suele ser frecuente que haya otros usuarios con roles de administrador (restringido)... en tal caso, es el Administrador absoluto, quien sólo puede otorgar-retirar privilegios de Administrador (restringido)...
Hola NEBIRE,
ahhh.. vale. Muchas gracias. Bueno... mas notas que apuntar y un codigo que traducir......
Que haria sin esta buena paginas:? http://converter.telerik.com/
Bueno. Te queria preguntar de que maneras uno se podria aprovechar de la vulnerabilidad de que la contrasena estuviera en un fichero de texto?
Y unas preguntas mas:
1) A lo que en realidad iba en todo este post era esto:
En realidad que haria lograr loguearme al servidor sin saber la contrasena usando algo como una interpolacion de string.
Osea que pudiera escribir esto:
/login "$"{contrasena}
Pero no funciona. Trato de hacer una referencia a la variable contrasena despues de haber escrito /login para poder loguearme sin saber la contrasena. Pero como puedo hacer esto? No se como incluir dentro de un string una referencia de una variable.
2) Los Querys solo se pueden hacer desde el cliente si el servidor es programado para recibir Querys?
Gracias y saludos
Cita de: TickTack en 24 Julio 2017, 21:19 PMBueno... mas notas que apuntar y un codigo que traducir......
Pues apunta una nota más que no te han dicho:
Cuando se trata de exponer contraseñas e información confdencial en general, en lugar de la clase
System.String deberías utilizar la clase
System.Security.SecureString para potenciar la seguridad de tu aplicación previniendo que los strings puedan ser almacenados por más tiempo del necesario en la memoria de la aplicación y por consiguiente intentar prevenir que un "intruso" (llámese hacker) pueda obtener dicha información mediante estrategias de ing. inversa que impliquen leer secciones de memoria.
Al utilizar la clase
System.Security.SecureString para almacenar un string confidencial, el string se almacena en un bloque de memoria no administrada, se cifra el bloque de memoria mediante la función Win32
RtlEncryptMemory (u otra función no documentada), y cuando el string ya no es necesario internamente se llama a la función Win32
ZeroMemory para llenar el bloque de memoria con ceros, es decir, para limpiar el bloque de memoria de cualquier rastro de nuestra información confidencial.
Todo ello lo puedes comprobar analizando la referencia del código fuente de .NET Framework:
- http://referencesource.microsoft.com/#mscorlib/system/security/securestring.cs,77d68ea938f47705
Aparte, si hablamos de un escenario donde hay un cliente y un servidor, y tú envias la contraseña descifrada pues... creo que el riesgo de intercepción de datos es más que evidente. Una idea genial y eficiente al 100% (sino al 99,9%) sería cifrar el string de nuestra instancia
SecureString mediante una clave pública otorgada por el servidor, de esta manera el string original nunca se vería expuesto en la transmisión de datos remota entre cliente y servidor, ni en la memoria administrada de la aplicación, por ende tampoco en el
GarbageCollector de .NET, y solamente el servidor podria descifrar el string cifrado mediante la clave pública otorgada.
Esta sugerencia que acabo de explicar es exactamente lo que hace
Microsoft en el mimebro
PSRemotingCryptoHelper.EncryptSecureStringCore().
- http://referencesource.microsoft.com/#System.Management.Automation/System/Management/Automation/Internal/PSRemotingCryptoHelper.cs
En fin. Dicha clase,
System.Security.SecureString tiene ciertas diferencias de uso en comparación con la clase
System.String, de hecho no se asemejan en nada. Para que te hagas una idea, se asemenja bastante más a la clase
System.Text.StringBuilder en el modo de empleo.
- SecureString Class | MSDN (https://msdn.microsoft.com/en-us/library/system.security.securestring(v=vs.110).aspx)
- How secure is SecureString? | MSDN (https://msdn.microsoft.com/en-us/library/system.security.securestring(v=vs.110).aspx#HowSecure)
Saludos
Hola mis amigos!!!!!!!!!!!
Tanto tiempo!!!!
Y si!! Ya hice mi servidor!!!!
La parte mas put* era la de concatenar algunos datos con un hash.
Esa parte no la hice. Me era demasiado trabajo. Sin agregar que no tengo nada de experiencia en hacer esas cosas no el conocimiento..... Suficiente con los otros complicados puntos para mi que ustedes me mencionaron......
Pero ahora me interesa lo que menciono Elektro...
Como puedo yo obtener strings almacenados en la memoria de una aplicacion mediante sockets o TCP usando estragias de ingenieria inversa que impliquen leer secciones de memoria?
Existen tales estrategias en relidad? Realmente se puede de esta manera interceptar datos?
Porque para me es inpensable que se pueda. Y si se puede... entonces cómo?
Me intereso bastante lo que escribiste....
Gracias y saludos
Hola a todos,
les escribo de nuevo porque todavia no se me aclaro la duda de como hacer que un servidor interprete lo que yo quiera como una variable.
Es decir, si el servidor espera que escriba /login <contrasena>, yo no se la contrasena. Pero se que en el programa hay una variable que se llama contrasena y que esa variable contiene la contrasena al ser comparada por lo que yo puse.
Es decir, si pongo /login 8888 y la contrasena es 1234 el programa compara la verdadera contrasena (que esta en la variable contrasena) con lo que yo puse.
Pero como puedo hacer para poner la variable contrasena dentro del comando y esperar que el servidor la interprete como tal?
Asi /login "contrasena"?
Porque se que Visual Studio filtra este tipo de enganos. Pero como lo hace?
Gracias y saludos
No estoy seguro de haberte entendido en esta parte (como tampoco en esta parte te entendí la otra vez).
...pero vamos intentanto elucubrar, creo que lo que tu pretendes es decirle que no le quieres pasar un texto, si no una variable... esto es, un texto pero no específicamente escrito por tí, si no obtenido de alguna parte en la memoria...
En realidad, se puede (pasar un string que reside en memoria), porque nunca se pasa un texto, lo que una variable esconde en realidad suele ser la dirección de dicha variable, eso es lo que suele pasarse en parámetros que no son de tipo primitivo (byte, integer, char, buleano, etc...), como es el "string", y que aún siendo considerado un tipo primitivo, por admitir la posibilidad de diferente tamaño, y que dicho tamaño puede ser considerablemente más grande que el ancho de palabra del bus de datos del procesador, se pasa su dirección.
Más aún, cuando escribes una función todos sus parámetros, han de ser de no más de 1,2,4,8 bytes (8bytes = 64 bits), luego todo aquella variable cuyos datos sea más largo, no se puede pasar su valor intrínseco, en parámetros, si no la dirección de memoria donde yace dicho valor. Decir variable, equivale en la práctica a señalar su dirección.
...entonces (y resumiendo), para hacer lo que tu quieres necesitas pasarle la dirección de memoria donde el servidor aloja esa 'variable' de nombre 'contrasena'... aún suponiendo que tanto el cliente como el servidor estén en el mismo equipo, tampoco es garantía de que sea posible, de hecho no es que pueda o no ser posible, si no que debería ser imposible salvo que esté muy mal programado. Me explico...
Tengo el servidor esperando un login de un cliente:
Buleano = Login(alias, contraseña)
Esta línea de pseudocódigo la resumiríamos como que un cliente invoca la función 'login pasándole dos parámetros, el nombre del cliente y una contraseña y a su vez el servidor le responde con un buleano indicando si el login tuvo éxito...
Bien, si lo miras bien, la memoria no tiene ahora mismo (en el momento de ser invocada la función), la contraseña de nadie, solo cuando recibe una llamada de login, mirando el alias, es cuando verificando que exista un usuario registrado con el mismo, es cuando puede optar a verificar si coincide la contraseña recibida, con lo que sea que esté alamcenado, pero como en ese momento de ser llamada la función ya debes haberle pasado la contraseña (la dirección donde se localiza el string), no podrá ser la del usuario 'alias' indicado, porque aún no se ha 'extraído', de parte ninguna... lo hará cuando haya validado el alias y deba comparar contraseñas o lo que sea que tenga almacenado.
Y como ya te indiqué (creo recordar) en un mensaje anterior... yo no compararía contraseña con contraseña en crudo, sino hash contra hash, y el hecho de que el hash deba ser calculado a través de la 'contraseña' recibida implica que no tendrías modo de falsificar una dirección, porque una vez dentro de la función lo recibido como 'contraseña' sufre una alteración por una función hash... y se compara contra el hash que consta en la base de datos (hash que se crea y guarda en la BD, cuando el usuario se registra).
Debe quedarte claro, que un login no debe remitirse a comparar dos cadenas de texto en crudo como si un usuario estuviera buscando un ítem en una lista.
Hashsear es algo intermedio entre cifrado y codificado, pero a diferencia de ambos, es irreversible, luego exige llevar ambos términos a ser hasheados antes de poderse comparar, con lo cual, jamás se ve en claro el texto original.
...yendo más lejos, al registrarse, ni siquiera guardaría el hash de la contraseña (si del alias), sino que guardaría, el hash de concatenar el hash del alias y la contraseña. Como señalo aquí:
Buleano = Funcion Registro(alias, contraseña)
string h, hPwd, hUser
hUser = Hashing(alias)
Si Existe(hUser)= FALSE luego
hPwd= Hashing(contraseña)
h = Hashing(hUser + hPwd)
Insert into tblRegistro Value (hUser, h, GetTime) //Now
Devolver TRUE
fin si
Fin funcion
En resumen, si no manejas directamente nunca más que durante el registro en usuario y la contraseña en la parte del servidor, no hay posibilidad de 'capturar' dicho dato y como al pasarle a la función de login una alias y una contraseña, dichos string son pasados a una función de hashing, quedará modificado, por lo que incluso aún habiendo extraído previamente de la base de datos el dato que se guarda, no es posible saltarse el asunto tal como tu pretendes hacerlo.
Hola NEBIRE,
gracias por tu respuesta.
Ok, entonces es imposible. Pero para asegurarme de que nos entendimos bien te quiero dejar
algo.
Lo que te queria dejar es esto:
https://github.com/AresChat/sb0t/blob/master/core/AccountManager.cs#L107
Se trata del metodo SecureLogin. Es a aso a lo que te referias, cierto?
Como puedes ver, tambien hay esta linea dentro del metodo:
byte[] pwd = sha1.ComputeHash(Encoding.UTF8.GetBytes(owner));
Es eso a lo que te referias con "hash contra hash"?
Gracias y saludos
PD.: Si realmente es imposible, entonces eso es bueno.
Si, más o menos...
Cada cual siempre pondrá más o menos capas de seguridad y hará cosas a su manera, pero es básico no guardar nombres ni contraseñas si no un hash de los mismos a lo sumo, o incluso como te decía un hash del hash concatenado a otra cosa...
Fíjate que tras esa línea luego aparece esta:
pwd = sha1.ComputeHash(SecurePassword(pwd, client.Cookie, ip));
...que invocas a la función SecurePassword donde como te indicaba concatena algún string más (en este caso, la cookie y la ip aunque delante, da igual al caso) y devuelve el array de bytes.
private static byte[] SecurePassword(byte[] password, uint cookie, IPAddress ip)
{
List<byte> buffer = new List<byte>();
buffer.AddRange(BitConverter.GetBytes(cookie));
buffer.AddRange(ip.GetAddressBytes());
buffer.AddRange(password);
return buffer.ToArray();
}
El único modo de lograr algo, es intervenir el propio proceso, para lo cual debes tener bien infectado el equipo y además conocerlo en cierta medida. Si al final del proyecto, usas algún sistema para ofuscar el código antes de compilarlo mejor, cualquiera que quisiere desentrañarlo, lo tendrá más difícil y si al final es un programa que usarán pocos cientos o miles d epersonas, lo más probable es que nadie pierda el tiempo con ello. Un hacker perderá el tiempo cuando haya cierto 'interés', potencialmente millones de personas... a menos que maneje dinero en cantidades sustanciales...
Sólo indicarte que no es estrictamente necesario que utilices un hash complejo o que te cueste entender, para tus propósitos un MD5, incluso un MD4 u otro vale... de hecho incluso sería más acertado tener tu propia función hash, pero si te acomoda las funciones que incluye NET (tiene varias), listo...
También te señalo que no escatimes en hacer llamadas de una función a otra, es decir no dejes que una única 'megafunción' realice todo el login, mucho mejor si distribuyes la carga de trabajo entre varias funciones... aparte de serte más fácil el mantenimiento y cambio del código, también complicas a posibles intrusos (si los llegare a haber)...