Spanish (Español) translation by Alfonso Mora (you can also view the original English article)



Bienvenido a la serie de Construcción su Startup con PHP serie, que está guiando a los lectores a través del lanzamiento de una startup real, Meeting Planner. Cada episodio información diversa codificación y desafíos de negocios, con ejemplos detallados que se puede utilizar para aprender.
Introducción
Recientemente, presente a usted Yii Simple Generación de la API REST y Nuevo "RESTful" Servicio API de Meeting Planner. En aquel momento, he mencionado que estas API fueron aseguradas solamente libremente. Sin duda, había un secreto compartido entre el cliente y el servidor, pero hubo un par de problemas.
En primer lugar, los tokens de usuario y clave secretos se transmitieron repetidamente en parámetros de consulta de llamadas SSL. Y sin otros verificación de autenticidad de los datos, permitiendo un ataque medio persona.
En el episodio de hoy, te guiará a través de cómo asegura la API contra estas debilidades para una API más robusta.
Si has estado leyendo nuestra serie de startup probablemente ya has probado Meeting Planner y Simple Planner pero si no, por favor, hazlo. Programar una reunión es muy fácil:
Como de costumbre, yo te participando en los comentarios, así que por favor ofrece sus pensamientos. También puede contactarme en Twitter @lookahead_io. Siempre estoy especialmente intrigado si quieres sugerir nuevas funciones o temas para futuros tutoriales.
Como recordatorio, todo el código para el Meeting Planner está escrito en el marco de Yii2 para PHP. Si desea más información sobre Yii2, revisa nuestra serie paralelo Programación con Yii2.
La Seguridad Inicial de la API
Vamos a empezar por echar un vistazo a la primera seguridad de API codifiqué. Te Presumimos que hay una aplicación móvil que he compartido con un $app_id
y $app_secret
. Se aceptan sólo API que llaman con esas teclas.
Por ejemplo, la aplicación intenta registrar su dueño, probablemente un nuevo usuario de Meeting Planner:
1 |
public function actionRegister($app_id='', $app_secret='', |
2 |
$source='',$firstname ='',$lastname='', |
3 |
$email = '',$oauth_token='') { |
4 |
Yii::$app->response->format = Response::FORMAT_JSON; |
5 |
// verify app_id and app_key
|
6 |
if (!Service::verifyAccess($app_id,$app_secret)) { |
7 |
// to do - error msg
|
8 |
return false; |
9 |
}
|
La aplicación llama anteriormente actionRegister
a través de https://api.meetingplanner.io/user-token/register/ con argumentos como sigue:
-
$app_id
y$app_secret
para la autenticación -
$source = 'facebook'
para el servicio de OAuth estamos usando y acompañando a$oauth_token
de que el servicio -
$email
,$firstname
y$lastname
proporcionada a través de OAuth
Todos aquellos son argumentos de consulta tales como:
1 |
https://api.meetingplanner.io/user-token/register/?app_id=777&app_secret=imwithher&source=facebook&oauth_token=zuckerburger&email=tom@macfarlins.com&firstname=thomas&lastname=macfarlins |
Service::verifyAccess($app_id,$app_secret)
busca las llaves para autenticar la llamada como se muestra a continuación:
1 |
class Service extends Model |
2 |
{
|
3 |
public static function verifyAccess($app_id,$app_secret) { |
4 |
if ($app_id == Yii::$app->params['app_id'] |
5 |
&& $app_secret == Yii::$app->params['app_secret']) { |
6 |
Yii::$app->params['site']['id']=SiteHelper::SITE_SP; |
7 |
return true; |
8 |
} else { |
9 |
return false; |
10 |
}
|
11 |
}
|
Porque las claves y los datos fueron enviados a través de SSL, son bastante seguras pero no invencible. Tampoco lo es la clave secreta seguridad en los iPhones de los usuarios para algunos.
¿Cómo podemos hacer esto más seguro? Aquí están algunas ideas:
- No transmitir nunca la clave secreta por Internet.
- No transmitir los datos como parámetros de URL que puede ser que aparezca en los registros de servidor.
- Firmar todos los datos para verificar su exactitud.
Estas son las prácticas realmente estándar que se usan para fijar las API.
Nota: Un ejemplo del riesgo de transmisión de datos que pudieran estar expuestos en los registros de servidor sería el correo electrónico y el Facebook OAuth token. Si encontró en los registros, estos podrían utilizarse con la API de Facebook para acceder a la cuenta de Facebook de alguien.
Implementar la Mejor Seguridad de API
Usando Firmas Hash
En primer lugar, voy a dejar de transmitir la $app_secret
. En cambio, firmamos los datos salientes con él antes de hacer una llamada a la API.
Tan ordenar alfabéticamente las variables y concatena en una cadena como esta:
1 |
$data = $email.$firstname.$lastname.$oauth_token.$source; |
Lo que Resulta en:
1 |
$data = 'tom@macfarlins.comthomasmacfarlinszuckerburgerzuckerburger' |
Entonces, a hash a los datos con hash_hmac de PHP y el algoritmo sha256
con nuestra clave secreta.
1 |
$signature = hash_hmac('sha256', |
2 |
$data,Yii::$app->params['app_secret']); |



Esto crea un código hash único basado en los argumentos de la llamada a la API y la clave secreta compartida:
1 |
$signature => 9f6d2f7dd7d674e85eff51f40f5f830787c37d84d4993ac9ccfea2800285bd02 |
Ahora, podemos hacer una llamada a la API sin transmitir la clave secreta. En cambio, nos transmiten la firma de los datos de hash anteriores.
He estado usando a Postman para probar la API, pero también se puede utilizar cURL:



Aquí está el código API recepción que respondieron a la llamada anterior:
1 |
public function actionRegister($app_id='', $source='',$firstname ='',$lastname='',$email = '',$oauth_token='',$sig='') { |
2 |
Yii::$app->response->format = Response::FORMAT_JSON; |
3 |
$sig_target = hash_hmac('sha256',$email.$firstname.$lastname.$oauth_token.$source,Yii::$app->params['app_secret']); |
4 |
if ($app_id != Yii::$app->params['app_id'] && $sig==$sig_target) { |
5 |
return 'it worked!'; |
6 |
} else { |
7 |
return 'failed!'; |
8 |
}
|
9 |
Además, he revisado la última vez, cada usuario recibe su propio token cuando tienen acceso a planificador de la reunión a través de la API, por ejemplo, mediante su teléfono móvil. Así, posteriores al registro, puede firmar llamadas con su ficha individual y no es necesario transmitir la clave secreta de la solicitud o ficha individual del usuario.
Envío de Datos en las Cabeceras HTTPS
A continuación, a migrar envío de datos en las cabeceras. Puede hacer esto fácilmente con Postman o cURL. Aquí está Postman:



Y aquí está cURL:
1 |
public function actionCurl($sig) { |
2 |
$ch = curl_init(); |
3 |
curl_setopt($ch, CURLOPT_URL,"http://localhost:8888/mp-api/user-token/register?sig=".$sig); |
4 |
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); |
5 |
$headers = [ |
6 |
'app_id: '.'imwithher', |
7 |
'email: '.'tom@macfarlins.com', |
8 |
'firstname: '.'thomas', |
9 |
'lastname: '.'macfarlins', |
10 |
'oauth_token: '.'zuckerburger', |
11 |
'source: '.'facebook', |
12 |
];
|
13 |
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); |
14 |
$server_output = curl_exec ($ch); |
15 |
var_dump($server_output); |
16 |
curl_close ($ch); |
17 |
}
|
Aquí está el código receptor que obtiene los datos de API de encabezados HTTPS:
1 |
public function actionRegister($sig='') { |
2 |
Yii::$app->response->format = Response::FORMAT_JSON; |
3 |
$headers = Yii::$app->request->headers; |
4 |
$email= $headers->get('email'); |
5 |
$firstname= $headers->get('firstname'); |
6 |
$lastname= $headers->get('lastname'); |
7 |
$oauth_token= $headers->get('oauth_token'); |
8 |
$source = $headers->get('source'); |
9 |
if ($headers->has('app_id')) { |
10 |
$app_id = $headers->get('app_id'); |
11 |
}
|
12 |
$sig_target = hash_hmac('sha256',$email.$firstname.$lastname.$oauth_token.$source,Yii::$app->params['app_secret']); |
13 |
if ($app_id != Yii::$app->params['app_id'] && $sig==$sig_target) { |
14 |
return 'it worked!'; |
15 |
} else { |
16 |
return 'failed!'; |
17 |
}
|
En el Cierre
Comenzamos hoy con los siguientes objetivos:
- No transmitir nunca la clave secreta por Internet.
- No transmitir los datos como parámetros de URL que puede ser que aparezca en los registros de servidor.
- Firmar todos los datos para verificar su exactitud.
Y hemos logrado todos estos objetivos con modestos cambios en nuestro código API. Fue muy divertido hacer estos cambios y ver cómo fácilmente podemos mejor asegure una API. Espero que disfrutado siguiendo junto con el episodio de hoy.
Regularmente de supervisar los comentarios, así que por favor únase a la discusión. Usted puede también alcanzarme en Twitter @lookahead_io directamente. Y, por supuesto, para próximos tutoriales aquí en la Serie Construyendo tu Startup con PHP.
Si no antes, trate de programar una reunión en el Meeting Planner y quisiera saber lo que piensas. Agradezco especialmente a peticiones.