Can post to /members/api/send-magic-link without integrityToken

Fill out the following bug report template with as much detail as possible!

Are you sure this is a bug? If you just need help, post in the developer help category. If it’s a feature request, head to the ideas category.


Issue Summary

On my self-hosted Ghost I have suspicious member sing-ups. The logs indicate that the /members/api/send-magic-link got posted to without prior call to get the integrity token.

Indeed I can post to it with integrityToken payload set to null and get the email.

I expected the link sent to report error

Steps to Reproduce

  1. Post like
    curl ‘https://theescritos.tz/members/api/send-magic-link/
    –compressed
    -X POST
    -H ‘User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:151.0) Gecko/20100101 Firefox/151.0’
    -H ‘Accept: /
    -H ‘Accept-Language: de,en-US;q=0.9,en;q=0.8’
    -H ‘Accept-Encoding: gzip, deflate, br, zstd’
    -H ‘Referer: https://theescritos.tz/
    -H ‘Content-Type: application/json’
    -H ‘Origin: https://theescritos.tz
    -H ‘Sec-GPC: 1’
    -H ‘Connection: keep-alive’
    -H ‘Cookie: _ga_7B8J6YSS1K=GS2.1.s1782648315$o1$g1$t1782648341$j34$l0$h0; _ga=GA1.1.797046755.1782648315’
    -H ‘Sec-Fetch-Dest: empty’
    -H ‘Sec-Fetch-Mode: cors’
    -H ‘Sec-Fetch-Site: same-origin’
    -H ‘Priority: u=4’
    -H ‘Pragma: no-cache’
    -H ‘Cache-Control: no-cache’
    -H ‘TE: trailers’
    –data-raw ‘{“email":"me@example.com”,“emailType”:“subscribe”,“labels”:,“autoRedirect”:true,“urlHistory”:[{“path”:“/”,“time”:1782648314943,“referrerSource”:null,“referrerMedium”:null,“utmSource”:null,“utmMedium”:null,“utmCampaign”:null,“utmTerm”:null,“utmContent”:null,“referrerUrl”:null}],“integrityToken”:null}’
  2. recieve email

Setup information

Ghost Version

6.45.0

Node.js Version
v22.23.1

How did you install Ghost?

docker-less

Provide details of your host & operating system

ubuntu 24.04 LTS

Database type
MySQL 9

How fun − I just wrote about that earlier today:

If you’re using Docker, add that environment variable to your container:

verifyRequestIntegrity=true

That will enforce them and block all requests that don’t have it with a clean 400 error.

Thank you.

I must admit the missing integrity check is a minor problem for me.
If the token can be access via a simple anonymous GET to a public API is just a minor complication for an attacker’s script.

And currently it seems to be a script POST-ing to the magic-link URL to trigger the e-mail confirmation. I assume as a GET to / and just the POST are logged for the same IP

I’m more worried that like 10s later a request to the sign-up URL is issued from a different IP
This time more likely from a browser, as all resources of the Page including css, etc are requested.

Yeah, totally.

But the economics of spam work in your favour here. It is just a dumb GET request, but it does mean double the amount of requests now.

And as harsh as it sounds, spammers will likely focus on other Ghost sites that don’t force the integrity token.

Once I have forced them on Magic Pages they backed off within 1-2 days across 1,400 sites.

thanks I am just not using docker for my deployment

Yeah, but whichever way you configured Ghost, adding that configuration flag (either via the config.production.json or environment variables) will activate it.