php - bearer - Asegurando una API REST accesible desde Android
php token authentication (5)
Estamos creando un juego para Android, que necesita acceso a servicios web, por lo que escribimos una API RESTful en PHP que se ejecuta en nuestro propio servidor. Lo que ofrece la API es: crear usuario, iniciar sesión, descargar juegos, recuperar la lista de juegos, enviar puntajes, etc. Ahora estoy pensando, si algún usuario experimentado obtiene el formato URL de la API, él / ella podrá Para destruir el sistema de muchas maneras:
- Cree un script y ejecútelo para crear usuarios automáticos. Creo que puedo evitarlo mediante CAPTCHA o algo así. Pero una vez más, el captcha molestará a los jugadores del juego.
- El usuario malintencionado inicia sesión con su navegador, descarga el juego y luego envía la puntuación como desee, todo a través de la API, simplemente escribiéndola desde su navegador. Asumo que el usuario malintencionado de alguna manera sabe las direcciones URL de API a las que debe llamar: detectando cuando la aplicación estaba realizando solicitudes HTTP.
- Necesito asegurarme de que las solicitudes se realicen solo desde el dispositivo Android que instaló el juego. (El juego será gratis)
Ahora, ¿cómo puedo prevenir tales abusos?
Creo que nunca podrás ocultar las URL que llama la aplicación (si estoy ejecutando un teléfono Android root, debería poder detectar todo el tráfico de la red)
Pero tu verdadero problema es que necesitas autentificar tu api de alguna manera.
Una forma sería implementar OAUTH, pero quizás esto sería una exageración.
Si quieres un mecanismo simple, ¿qué tal esto?
- crear una clave secreta
- compilar la solicitud de API (por ejemplo, https://my.example.com/users/23?fields=name,email )
- hash esta ruta de solicitud + más su clave secreta (por ejemplo, md5 (url + secret_key) == "a3c2fe167")
- agregue este hash a su solicitud (ahora es https: // .....? fields = nombre, correo electrónico y hash = a3c2fe167 )
- en el extremo de la API, haga la misma conversión (elimine el hash param)
- Comprueba el md5 de la url y la clave secreta.
Mientras el secreto permanezca en secreto, nadie puede falsificar sus solicitudes.
Ejemplo (en pseudocódigo):
Lado de Android:
SECRET_KEY = "abc123"
def call_api_with_secret(url, params)
# create the hash to sign the request
hash = MD5.hash(SECRET_KEY, url, params)
# call the api with the added hash
call_api(url+"&hash=#{hash}", params)
end
Lado del servidor:
SECRET_KEY = "abc123"
def receive_from_api(url, params)
# retrieve the hash
url_without_hash, received_hash = retrieve_and_remove_hash(url)
# check the hash
expected_hash = MD5.hash(SECRET_KEY, url_without_hash, params)
if (expected_hash != received_hash)
raise our exception!
end
# now do the usual stuff
end
Las soluciones que otros han presentado aquí se llaman seguridad a través de la oscuridad . Básicamente, están tratando de ocultar el protocolo y ocultar la implementación. Esto podría funcionar hasta que alguien lo suficientemente capaz desarme la aplicación y realice una ingeniería inversa del protocolo. Los hackers son muy capaces en eso.
La pregunta es si tu aplicación vale la pena descifrarla. Los esquemas como iTunes, DVD o la red de Sony PS3 obviamente valieron la pena. El enfoque de la oscuridad podría funcionar si nadie es capaz de resolver problemas. Simplemente no te engañes a ti mismo que no es factible.
Como no puede confiar en el dispositivo o su aplicación, debe confiar en el usuario. Para confiar en el usuario, necesita un sistema de identificación y autorización de usuario. Básicamente un login a su aplicación. En lugar de eso, utiliza su propio sistema de identificación (inicie sesión con correos electrónicos de confirmación, etc.), use un sistema de terceros: OpenID (cuentas de Google) o OAuth (facebook, twitter). En el caso de Facebook use el esquema de autenticación del lado del servidor.
Lo que yo haría:
- Permitir a los usuarios jugar libremente hasta que quieran "guardar" los resultados en el servidor.
- Antes de guardar sus resultados haga que inicien sesión a través del método mencionado anteriormente.
- Utilice HTTPS para enviar los datos a su servidor. Compre un certificado ssl de CA confiable, para que no tenga que lidiar con certificados autofirmados.
Mencionaste a los usuarios fingiendo las puntuaciones más altas. Esto aún podría suceder si sus usuarios están autenticados. Cuando el juego está cargando las puntuaciones más altas es posible que desee que también cargue una prueba de la puntuación. Por ejemplo, Score 20100 de 103 bichos aplastados, 1200 millas voladas, nivel 3 alcanzado y 2 cerezas fueron consumidas. Esto de ninguna manera es perfecto, pero cubriría la fruta de baja altura.
Lo primero que debes hacer es tener usuarios autenticados. ID de usuario / contraseña / token de sesión, etc., vea si puede encontrar algunos marcos existentes. Una vez que tenga la autenticación de usuario, asegúrese de poder hacerlo de forma segura con TLS o similar.
Por lo que sé, no hay forma de que su servidor pueda estar seguro de que la solicitud proviene de su aplicación (solo se trata de bits en paquetes), pero al menos puede dificultar que alguien sea malicioso.
- Cree un secreto en su aplicación (como lo sugieren otras respuestas, clave, sal de hash, etc.)
- Genere una ID única en la primera ejecución de la aplicación después de la instalación y realice el seguimiento junto con el usuario que inició sesión. Los detalles sobre esto y la identificación única del dispositivo (por qué no usarlo) se pueden encontrar en el blog de Android
- Algunas ideas discutidas en esta publicación ¿Cómo asegurar / determinar que una publicación proviene de una aplicación específica que se ejecuta en un iPhone / iTouch?
- Verifique agente de usuario
Si realmente quiere asegurar la conexión, entonces tendrá que usar la criptografía de clave pública, por ejemplo, RSA. El dispositivo cifrará la información de inicio de sesión utilizando la clave pública y, en el extremo del servidor, deberá descifrarla utilizando la clave privada. Después de iniciar sesión, el servidor enviará una clave de token / encriptación (la respuesta será un JSON encriptado o algo así) y el dispositivo almacenará eso. A partir de entonces, siempre que la sesión no haya caducado, el dispositivo enviará toda la información cifrada con ese token. Para estas solicitudes no debes usar RSA porque eso tomará más tiempo. Puede usar AES256 (que es un cifrado de clave privada popular) con esa clave de cifrado recibida del servidor para cifrar sus solicitudes.
En aras de la simplicidad, puede eliminar RSA por completo (si no está enviando información de pago) y hacer todo usando AES256 con una clave privada. Los pasos deben ser:
- Cifre cada solicitud saliente con una clave privada.
- Convierte la cadena cifrada en una cadena base 64.
- URL codifica la cadena codificada base 64.
- Envíalo.
En el extremo del servidor
- ¿Decodificar la base 64?
- Descifrar utilizando la clave privada.
Su solicitud debe llevar una firma (por ejemplo, la clave de cifrado adjunta como sal) para que sea posible identificarla después de descifrarla. Si la firma no está presente, simplemente descarte la solicitud.
Para enviar respuestas haz lo mismo.
El SDK de Android debe tener métodos para cifrar con la codificación AES256 y Base 64.
Siga estas pautas del equipo de Android para asegurar su backend, utilizando los tokens de Oauth proporcionados a través de las API de Google.