python - ¿Cómo validar una solicitud de la API de Slack?
Tengo una aplicación de Slack que está enviando a un servicio escrito en Typescript que reenvía el mensaje a mi script de Python donde estoy tratando de validar la solicitud. Sin embargo, por alguna razón, la validación siempre falla. El código relevante en Typescript:
const rp = require('request-promise');
var qs = require('querystring')
export const handler = async (event: any, context: Context, callback: Callback): Promise<any> => {
const options = {
method: method,
uri: some_url,
body: qs.parse(event.body),
headers: {
signature: event.headers['X-Slack-Signature'],
timestamp: event.headers['X-Slack-Request-Timestamp']
},
json: true
};
return rp(options);
El código de Python (basado en este artículo) :
def authenticate_message(self, request: Request) -> bool:
slack_signing_secret = bytes(SLACK_SIGNING_SECRET, 'utf-8')
slack_signature = request.headers['signature']
slack_timestamp = request.headers['timestamp']
request_body = json.loads(request.body)['payload']
basestring = f"v0:{slack_timestamp}:{request_body}".encode('utf-8')
my_signature = 'v0=' + hmac.new(slack_signing_secret, basestring, hashlib.sha256).hexdigest()
return hmac.compare_digest(my_signature, slack_signature))
Estoy bastante seguro de que el problema es la forma en que estoy tomando el cuerpo, pero intenté varias opciones y todavía no tuve suerte.
¿Alguna idea?
Gracias, Nir.
Respuesta #1
Tuve el mismo problema. Mi solución fue analizar la carga útil para reemplazar '/' por %2F y ':' por %3A. No es explícito en el documento de Slack, pero si ves el ejemplo, así es como se muestra:
'v0:1531420618:token=xyzz0WbapA4vBCDEFasx0q6G&team_id=T1DC2JH3J&team_domain=testteamnow&channel_id=G8PSS9T3V&channel_name=foobar&user_id=U2CERLKJA&user_name=roadrunner&command=%2Fwebhook-collect&text=&response_url=https%3A%2F%2Fhooks.slack.com%2Fcommands%2FT1DC2JH3J%2F397700885554%2F96rGlfmibIGlgcZRskXaIFfN&trigger_id=398738663015.47445629121.803a0bc887a14d10d2c447fce8b6703c'
Ves que command y response_url se analizan.
Logré hacer que esto funcionara en Python. Veo que preguntas en Typescript, pero espero que este script de Python te ayude:
@app.route('/slack-validation', methods=['GET', 'POST'])
def slack_secutiry():
headers = request.headers
timestamp = request.headers['X-Slack-Request-Timestamp']
slack_payload = request.form
dict_slack = slack_payload.to_dict()
### This is the key that solved the issue for me, where urllib.parse.quote(val, safe='')] ###
payload= "&".join(['='.join([key, urllib.parse.quote(val, safe='')]) for key, val in dict_slack.items()])
### compose the message:
sig_basestring = 'v0:' + timestamp + ':' + payload
sig_basestring = sig_basestring.encode('utf-8')
### secret
signing_secret = slack_signing_secret.encode('utf-8') # I had an env variable declared with slack_signing_secret
my_signature = 'v0=' + hmac.new(signing_secret, sig_basestring, hashlib.sha256).hexdigest()
print('my signature: ')
print(my_signature)
return '', 200
Respuesta #2
Podría ser útil para ti revisar cómo se implementa la función de validación de solicitudes en el framework Bolt:
https://github.com/slackapi/bolt-python/blob/4e0709f0578080833f9aeab984a778be81a30178/slack_bolt/middleware/request_verification/request_verification.py
Tenga en cuenta que se implementa como un middleware, habilitado por defecto cuando instancia la aplicación (ver el atributo request_verification_enabled).
Puedes inspeccionar este comportamiento y/o cambiarlo si deseas validar las solicitudes manualmente:
app = App(
token=SLACK_BOT_TOKEN,
signing_secret=SLACK_SIGNING_SECRET,
request_verification_enabled=False
)
