Robocaller CAPTCHA

Stopping robocalls and spam using Asterisk

2019

Robocalls are a pain. It eventually got to the point where nobody would pick up the phone because 24 out of 25 calls would be junk. Since I build my telephone system with asterisk I had the ability to fix it. CAPTCHA is used to stop people from automating the abuse of web forms. With asterisk we can do something similar with telephones. To reduce the annoyance for regular callers we can also use asterisks database to remember who successfully verified.

Overview

Simply the system would do the following.

  • If a caller has previously passed the CAPTCHA let them through.
  • Otherwise present an IVR CAPTCHA with an audible prompt.
    • If the caller follows the prompt; add them to the database and let them through.
    • Otherwise hang up.

Capture

First we have to intercept the incoming call. In this example incoming calls are passed through the inbound-from-outside context. Calls for 321.654.0987 are then checked for a match in the whitelist database. If a match is found control flows to the inbound context. Otherwise control flows to the captcha context.

extensions.conf
[inbound-from-outside]

exten => _3216540987,1,NoOp()
 same => n,GotoIf(${DB_EXISTS(whitelist/${CALLERID(num)})}?inbound,s,1:captcha,s,1)
 same => n,Hangup

Captcha

If the caller isn’t in the database we present the captcha test. This can be made much more complicated with randomized digits and other bells and whistles, but that isn’t necessary with the current state of robocallers. Perhaps someday I will implement a 4 digit randomly generated CAPTCHA.

In this example we present the recorded message “To continue with your call press 6” stored in the audio file ‘custom/capcha’ and wait for a digit to be pressed or timeout after 20 seconds. If that digit is a 6 we go to correct, adding the caller ID number to the database whitelist, and passing though the call. If it’s not we go to invalid which plays an annoying noise, says goodbye, and then hangs up. In the case of a timeout we say goodbye and hang up.

extensions.conf
[captcha]

exten => s,1,Answer()
 same => n,Background(custom/captcha)
 same => n,Read(digit,,1,,,20)
 same => n,GotoIf($[${digit} == 6]?correct:invalid)
 same => n(invalid),NoOp()
 same => n,Zapateller()
 same => n,Playback(goodbye)
 same => n,Hangup
 same => n(correct),NoOp()
 same => n,Set(DB(whitelist/${CALLERID(num)})=1)
 same => n,Goto(inbound,s,1)
 same => n,Hangup

; Timeout
exten => t,1,Playback(goodbye)
 same => n,Hangup

Success

If the caller completes the captcha or has already verified we can complete the call as normal. A simple implementation could be as follows.

extensions.conf
[inbound]

exten => s,1,NoOp()
 same => n,Dial(SIP/some_client,30,tr)
 same => n,Goto(voicemail-ivr,s,1)
 same => n,Hangup

Thoughts

While this isn’t infallible it works. As far as I know we havent had a single robocall get though in the 2 years this has been running. As I mentioned before we can make the CAPTCHA dynamic with a larger number, but in reality a single static number works well enough for now.

Caller ID’s can also be spoofed, but the trouble to go though finding someone who has already passed the CAPTCHA is more then to just do it manually once. Which is still more time then any robocaller is going to put into it.

For now and until this enters regular use its more then enough to free me, and now you, from robocallers.

License

Any source in this article is released under the ISC License.