Búsqueda de sitios web

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
)

Artículos relacionados