[{"data":1,"prerenderedAt":861},["ShallowReactive",2],{"post-\u002Fblog\u002Fvoip-security-sip-hardening":3},{"id":4,"title":5,"author":6,"body":7,"category":844,"coverImage":845,"date":846,"description":847,"extension":848,"meta":849,"navigation":201,"path":850,"readingTime":269,"seo":851,"stem":852,"tags":853,"__hash__":860},"posts\u002Fblog\u002Fvoip-security-sip-hardening.md","VoIP Security: Hardening SIP Infrastructure Against Attacks","Tumarm Engineering",{"type":8,"value":9,"toc":830},"minimark",[10,14,18,23,26,81,85,95,100,110,120,124,130,134,141,147,150,154,157,209,273,288,292,295,378,385,389,392,465,469,472,477,521,526,579,584,618,622,625,687,690,694,823,826],[11,12,5],"h1",{"id":13},"voip-security-hardening-sip-infrastructure-against-attacks",[15,16,17],"p",{},"SIP infrastructure is one of the most actively attacked surfaces on the internet. Port 5060 UDP is probed constantly — automated scanners enumerate SIP extensions, brute-force credentials, and launch toll fraud attacks that can generate $10,000–$50,000 in carrier charges before anyone notices. This post covers concrete hardening steps that eliminate the easy attacks and make the hard ones expensive enough to be unprofitable.",[19,20,22],"h2",{"id":21},"threat-model","Threat Model",[15,24,25],{},"The attacks worth defending against, roughly in order of frequency:",[27,28,29,46,63,69,75],"ol",{},[30,31,32,36,37,41,42,45],"li",{},[33,34,35],"strong",{},"Extension enumeration"," — SIPvicious, svwar, and similar tools send OPTIONS or REGISTER requests to every extension from 100 to 9999 looking for valid usernames via ",[38,39,40],"code",{},"404 Not Found"," vs ",[38,43,44],{},"401 Unauthorized"," responses.",[30,47,48,51,52,55,56,55,59,62],{},[33,49,50],{},"Credential brute-force"," — After finding valid extensions, attackers try common SIP passwords: the extension number itself, ",[38,53,54],{},"1234",", ",[38,57,58],{},"0000",[38,60,61],{},"secret",".",[30,64,65,68],{},[33,66,67],{},"REGISTER flooding"," — Volumetric attacks that consume CPU processing authentication challenges.",[30,70,71,74],{},[33,72,73],{},"Toll fraud"," — Once authenticated (or by exploiting misconfigured contexts), attackers place calls to premium-rate numbers or international destinations they monetize.",[30,76,77,80],{},[33,78,79],{},"Media injection"," — Inserting RTP packets into established calls to eavesdrop or disrupt.",[19,82,84],{"id":83},"response-code-normalization","Response Code Normalization",[15,86,87,88,90,91,94],{},"The cheapest fix: stop leaking information through SIP response codes. A ",[38,89,44],{}," for a valid extension and a ",[38,92,93],{},"403 Forbidden"," for an invalid one tells attackers which extensions exist. Return the same response code for both:",[96,97,99],"h3",{"id":98},"kamailio","Kamailio",[101,102,107],"pre",{"className":103,"code":105,"language":106},[104],"language-text","request_route {\n    if (is_method(\"REGISTER\")) {\n        if (!www_authenticate(\"mydomain.com\", \"subscriber\")) {\n            # Always send 401, never 403 or 404\n            www_challenge(\"mydomain.com\", \"1\");\n            exit;\n        }\n        if (!save(\"location\")) {\n            sl_reply_error();\n            exit;\n        }\n        sl_send_reply(\"200\", \"OK\");\n        exit;\n    }\n}\n","text",[38,108,105],{"__ignoreMap":109},"",[15,111,112,113,116,117,119],{},"Kamailio's ",[38,114,115],{},"www_authenticate"," returns false for both wrong password and unknown user. The caller sees only ",[38,118,44],{}," either way.",[96,121,123],{"id":122},"opensips","OpenSIPS",[101,125,128],{"className":126,"code":127,"language":106},[104],"route {\n    if (is_method(\"REGISTER\")) {\n        if (!www_authorize(\"\", \"subscriber\")) {\n            www_challenge(\"\", 0);\n            exit;\n        }\n        if (!save_contacts(\"location\")) {\n            send_reply(500, \"Internal Error\");\n            exit;\n        }\n    }\n}\n",[38,129,127],{"__ignoreMap":109},[19,131,133],{"id":132},"rate-limiting-with-pike-kamailio","Rate Limiting with pike (Kamailio)",[15,135,136,137,140],{},"The ",[38,138,139],{},"pike"," module tracks per-IP request rates and blocks sources that exceed your threshold:",[101,142,145],{"className":143,"code":144,"language":106},[104],"loadmodule \"pike.so\"\n\nmodparam(\"pike\", \"sampling_time_unit\", 2)\nmodparam(\"pike\", \"reqs_density_per_unit\", 30)\nmodparam(\"pike\", \"remove_latency\", 4)\n\nrequest_route {\n    if (!pike_check_req()) {\n        xlog(\"L_WARN\", \"Pike blocked $si:$sp - $rm\\n\");\n        exit;\n    }\n    # ... rest of routing\n}\n",[38,146,144],{"__ignoreMap":109},[15,148,149],{},"This blocks any IP sending more than 30 SIP requests per 2-second window. Legitimate SIP endpoints send a few REGISTER and OPTIONS per minute. An enumeration tool sends hundreds per second.",[19,151,153],{"id":152},"fail2ban-integration","fail2ban Integration",[15,155,156],{},"fail2ban reads log files and adds iptables rules to block offending IPs. Configure it to watch Kamailio or Asterisk logs:",[101,158,162],{"className":159,"code":160,"language":161,"meta":109,"style":109},"language-ini shiki shiki-themes github-light github-dark","# \u002Fetc\u002Ffail2ban\u002Ffilter.d\u002Fkamailio.conf\n[Definition]\nfailregex = .*\\[\u003CHOST>\\].*\"(REGISTER|INVITE|OPTIONS)\".*-> 401\n            .*\\[\u003CHOST>\\].*\"(REGISTER|INVITE|OPTIONS)\".*-> 403\n            .*pike blocked.*\\[\u003CHOST>\\]\n\nignoreregex =\n","ini",[38,163,164,172,178,184,190,196,203],{"__ignoreMap":109},[165,166,169],"span",{"class":167,"line":168},"line",1,[165,170,171],{},"# \u002Fetc\u002Ffail2ban\u002Ffilter.d\u002Fkamailio.conf\n",[165,173,175],{"class":167,"line":174},2,[165,176,177],{},"[Definition]\n",[165,179,181],{"class":167,"line":180},3,[165,182,183],{},"failregex = .*\\[\u003CHOST>\\].*\"(REGISTER|INVITE|OPTIONS)\".*-> 401\n",[165,185,187],{"class":167,"line":186},4,[165,188,189],{},"            .*\\[\u003CHOST>\\].*\"(REGISTER|INVITE|OPTIONS)\".*-> 403\n",[165,191,193],{"class":167,"line":192},5,[165,194,195],{},"            .*pike blocked.*\\[\u003CHOST>\\]\n",[165,197,199],{"class":167,"line":198},6,[165,200,202],{"emptyLinePlaceholder":201},true,"\n",[165,204,206],{"class":167,"line":205},7,[165,207,208],{},"ignoreregex =\n",[101,210,212],{"className":159,"code":211,"language":161,"meta":109,"style":109},"# \u002Fetc\u002Ffail2ban\u002Fjail.d\u002Fkamailio.conf\n[kamailio]\nenabled  = true\nport     = 5060,5061\nprotocol = udp\nfilter   = kamailio\nlogpath  = \u002Fvar\u002Flog\u002Fkamailio\u002Fkamailio.log\nmaxretry = 10\nfindtime = 300\nbantime  = 3600\naction   = iptables-allports[name=kamailio, protocol=all]\n",[38,213,214,219,224,229,234,239,244,249,255,261,267],{"__ignoreMap":109},[165,215,216],{"class":167,"line":168},[165,217,218],{},"# \u002Fetc\u002Ffail2ban\u002Fjail.d\u002Fkamailio.conf\n",[165,220,221],{"class":167,"line":174},[165,222,223],{},"[kamailio]\n",[165,225,226],{"class":167,"line":180},[165,227,228],{},"enabled  = true\n",[165,230,231],{"class":167,"line":186},[165,232,233],{},"port     = 5060,5061\n",[165,235,236],{"class":167,"line":192},[165,237,238],{},"protocol = udp\n",[165,240,241],{"class":167,"line":198},[165,242,243],{},"filter   = kamailio\n",[165,245,246],{"class":167,"line":205},[165,247,248],{},"logpath  = \u002Fvar\u002Flog\u002Fkamailio\u002Fkamailio.log\n",[165,250,252],{"class":167,"line":251},8,[165,253,254],{},"maxretry = 10\n",[165,256,258],{"class":167,"line":257},9,[165,259,260],{},"findtime = 300\n",[165,262,264],{"class":167,"line":263},10,[165,265,266],{},"bantime  = 3600\n",[165,268,270],{"class":167,"line":269},11,[165,271,272],{},"action   = iptables-allports[name=kamailio, protocol=all]\n",[15,274,275,276,279,280,283,284,287],{},"This bans any IP that triggers 10 auth failures within 5 minutes for 1 hour. Increase ",[38,277,278],{},"bantime"," to 86400 (24 hours) for persistent attackers; add a ",[38,281,282],{},"[kamailio-repeat]"," jail with ",[38,285,286],{},"bantime = -1"," (permanent) for IPs that return after banning.",[19,289,291],{"id":290},"enforcing-tls-for-sip-signaling","Enforcing TLS for SIP Signaling",[15,293,294],{},"Plain UDP SIP exposes authentication credentials (MD5 hashed, but crackable offline) and call metadata. Enforce TLS for all external endpoints:",[101,296,298],{"className":159,"code":297,"language":161,"meta":109,"style":109},"# Asterisk PJSIP transport\n[transport-tls]\ntype=transport\nprotocol=tls\nbind=0.0.0.0:5061\ncert_file=\u002Fetc\u002Fletsencrypt\u002Flive\u002Fpbx.example.com\u002Ffullchain.pem\npriv_key_file=\u002Fetc\u002Fletsencrypt\u002Flive\u002Fpbx.example.com\u002Fprivkey.pem\ncipher=ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384\nmethod=tlsv1_2\n\n[endpoint-tls-template]\ntype=endpoint\ntransport=transport-tls\nmedia_encryption=sdes\nmedia_encryption_optimistic=no\n",[38,299,300,305,310,315,320,325,330,335,340,345,349,354,360,366,372],{"__ignoreMap":109},[165,301,302],{"class":167,"line":168},[165,303,304],{},"# Asterisk PJSIP transport\n",[165,306,307],{"class":167,"line":174},[165,308,309],{},"[transport-tls]\n",[165,311,312],{"class":167,"line":180},[165,313,314],{},"type=transport\n",[165,316,317],{"class":167,"line":186},[165,318,319],{},"protocol=tls\n",[165,321,322],{"class":167,"line":192},[165,323,324],{},"bind=0.0.0.0:5061\n",[165,326,327],{"class":167,"line":198},[165,328,329],{},"cert_file=\u002Fetc\u002Fletsencrypt\u002Flive\u002Fpbx.example.com\u002Ffullchain.pem\n",[165,331,332],{"class":167,"line":205},[165,333,334],{},"priv_key_file=\u002Fetc\u002Fletsencrypt\u002Flive\u002Fpbx.example.com\u002Fprivkey.pem\n",[165,336,337],{"class":167,"line":251},[165,338,339],{},"cipher=ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384\n",[165,341,342],{"class":167,"line":257},[165,343,344],{},"method=tlsv1_2\n",[165,346,347],{"class":167,"line":263},[165,348,202],{"emptyLinePlaceholder":201},[165,350,351],{"class":167,"line":269},[165,352,353],{},"[endpoint-tls-template]\n",[165,355,357],{"class":167,"line":356},12,[165,358,359],{},"type=endpoint\n",[165,361,363],{"class":167,"line":362},13,[165,364,365],{},"transport=transport-tls\n",[165,367,369],{"class":167,"line":368},14,[165,370,371],{},"media_encryption=sdes\n",[165,373,375],{"class":167,"line":374},15,[165,376,377],{},"media_encryption_optimistic=no\n",[15,379,380,381,384],{},"Setting ",[38,382,383],{},"media_encryption_optimistic=no"," forces SRTP — calls fail rather than fall back to cleartext RTP. This is the right default for anything carrying confidential calls.",[19,386,388],{"id":387},"srtp-for-media","SRTP for Media",[15,390,391],{},"SIP over TLS protects signaling. SRTP protects the media. Enabling both closes the full eavesdropping path:",[101,393,395],{"className":159,"code":394,"language":161,"meta":109,"style":109},"# Kamailio: enforce SRTP by rejecting offers without crypto\nrequest_route {\n    if (is_method(\"INVITE\")) {\n        if (!has_body(\"application\u002Fsdp\")) {\n            sl_send_reply(\"400\", \"Bad Request\");\n            exit;\n        }\n        # Check for SRTP crypto line in SDP\n        if (!search_body(\"a=crypto:\")) {\n            sl_send_reply(\"488\", \"Not Acceptable Here\");\n            exit;\n        }\n    }\n}\n",[38,396,397,402,407,412,417,422,427,432,437,442,447,451,455,460],{"__ignoreMap":109},[165,398,399],{"class":167,"line":168},[165,400,401],{},"# Kamailio: enforce SRTP by rejecting offers without crypto\n",[165,403,404],{"class":167,"line":174},[165,405,406],{},"request_route {\n",[165,408,409],{"class":167,"line":180},[165,410,411],{},"    if (is_method(\"INVITE\")) {\n",[165,413,414],{"class":167,"line":186},[165,415,416],{},"        if (!has_body(\"application\u002Fsdp\")) {\n",[165,418,419],{"class":167,"line":192},[165,420,421],{},"            sl_send_reply(\"400\", \"Bad Request\");\n",[165,423,424],{"class":167,"line":198},[165,425,426],{},"            exit;\n",[165,428,429],{"class":167,"line":205},[165,430,431],{},"        }\n",[165,433,434],{"class":167,"line":251},[165,435,436],{},"        # Check for SRTP crypto line in SDP\n",[165,438,439],{"class":167,"line":257},[165,440,441],{},"        if (!search_body(\"a=crypto:\")) {\n",[165,443,444],{"class":167,"line":263},[165,445,446],{},"            sl_send_reply(\"488\", \"Not Acceptable Here\");\n",[165,448,449],{"class":167,"line":269},[165,450,426],{},[165,452,453],{"class":167,"line":356},[165,454,431],{},[165,456,457],{"class":167,"line":362},[165,458,459],{},"    }\n",[165,461,462],{"class":167,"line":368},[165,463,464],{},"}\n",[19,466,468],{"id":467},"dialplan-fraud-prevention","Dialplan Fraud Prevention",[15,470,471],{},"Toll fraud usually exploits a misconfigured dialplan context. Common mistakes:",[15,473,474],{},[33,475,476],{},"Never allow unauthenticated access to outbound routes:",[101,478,480],{"className":159,"code":479,"language":161,"meta":109,"style":109},"# WRONG — any SIP device that reaches this context can dial out\n[default]\nexten => _9.,1,Dial(PJSIP\u002F${EXTEN:1}@carrier)\n\n# RIGHT — only authenticated extensions reach from-internal\n[from-internal]\nexten => _9.,1,GoSub(sub-check-credit,s,1)\n same => n,Dial(PJSIP\u002F${EXTEN:1}@carrier)\n",[38,481,482,487,492,497,501,506,511,516],{"__ignoreMap":109},[165,483,484],{"class":167,"line":168},[165,485,486],{},"# WRONG — any SIP device that reaches this context can dial out\n",[165,488,489],{"class":167,"line":174},[165,490,491],{},"[default]\n",[165,493,494],{"class":167,"line":180},[165,495,496],{},"exten => _9.,1,Dial(PJSIP\u002F${EXTEN:1}@carrier)\n",[165,498,499],{"class":167,"line":186},[165,500,202],{"emptyLinePlaceholder":201},[165,502,503],{"class":167,"line":192},[165,504,505],{},"# RIGHT — only authenticated extensions reach from-internal\n",[165,507,508],{"class":167,"line":198},[165,509,510],{},"[from-internal]\n",[165,512,513],{"class":167,"line":205},[165,514,515],{},"exten => _9.,1,GoSub(sub-check-credit,s,1)\n",[165,517,518],{"class":167,"line":251},[165,519,520],{}," same => n,Dial(PJSIP\u002F${EXTEN:1}@carrier)\n",[15,522,523],{},[33,524,525],{},"Restrict international dialing by default:",[101,527,529],{"className":159,"code":528,"language":161,"meta":109,"style":109},"[from-internal]\n; Domestic only by default\nexten => _NXXNXXXXXX,1,GoSub(sub-dialout,s,1(${EXTEN}))\n\n; International requires explicit permission via DB lookup\nexten => _011.,1,GoSub(sub-check-international-permission,s,1(${CALLERID(num)}))\n same => n,GotoIf($[\"${PERMISSION}\" = \"yes\"]?allowed:denied)\n same => n(allowed),GoSub(sub-dialout,s,1(${EXTEN}))\n same => n(denied),Playback(ss-noservice)\n same => n,Hangup()\n",[38,530,531,535,540,545,549,554,559,564,569,574],{"__ignoreMap":109},[165,532,533],{"class":167,"line":168},[165,534,510],{},[165,536,537],{"class":167,"line":174},[165,538,539],{},"; Domestic only by default\n",[165,541,542],{"class":167,"line":180},[165,543,544],{},"exten => _NXXNXXXXXX,1,GoSub(sub-dialout,s,1(${EXTEN}))\n",[165,546,547],{"class":167,"line":186},[165,548,202],{"emptyLinePlaceholder":201},[165,550,551],{"class":167,"line":192},[165,552,553],{},"; International requires explicit permission via DB lookup\n",[165,555,556],{"class":167,"line":198},[165,557,558],{},"exten => _011.,1,GoSub(sub-check-international-permission,s,1(${CALLERID(num)}))\n",[165,560,561],{"class":167,"line":205},[165,562,563],{}," same => n,GotoIf($[\"${PERMISSION}\" = \"yes\"]?allowed:denied)\n",[165,565,566],{"class":167,"line":251},[165,567,568],{}," same => n(allowed),GoSub(sub-dialout,s,1(${EXTEN}))\n",[165,570,571],{"class":167,"line":257},[165,572,573],{}," same => n(denied),Playback(ss-noservice)\n",[165,575,576],{"class":167,"line":263},[165,577,578],{}," same => n,Hangup()\n",[15,580,581],{},[33,582,583],{},"Set per-account call limits:",[101,585,587],{"className":159,"code":586,"language":161,"meta":109,"style":109},"[sub-check-credit]\nexten => s,1,Set(ACTIVE_CALLS=${DB(calls\u002F${CALLERID(num)}\u002Factive)})\n same => n,GotoIf($[${ACTIVE_CALLS} >= 3]?overlimit)\n same => n,Return()\n same => n(overlimit),Playback(ss-noservice)\n same => n,Hangup()\n",[38,588,589,594,599,604,609,614],{"__ignoreMap":109},[165,590,591],{"class":167,"line":168},[165,592,593],{},"[sub-check-credit]\n",[165,595,596],{"class":167,"line":174},[165,597,598],{},"exten => s,1,Set(ACTIVE_CALLS=${DB(calls\u002F${CALLERID(num)}\u002Factive)})\n",[165,600,601],{"class":167,"line":180},[165,602,603],{}," same => n,GotoIf($[${ACTIVE_CALLS} >= 3]?overlimit)\n",[165,605,606],{"class":167,"line":186},[165,607,608],{}," same => n,Return()\n",[165,610,611],{"class":167,"line":192},[165,612,613],{}," same => n(overlimit),Playback(ss-noservice)\n",[165,615,616],{"class":167,"line":198},[165,617,578],{},[19,619,621],{"id":620},"anomaly-detection-spend-velocity-alerts","Anomaly Detection: Spend Velocity Alerts",[15,623,624],{},"Technical controls stop known attack patterns. Behavioral anomaly detection catches novel attacks. Track per-account spend velocity against your carrier CDR feed:",[101,626,630],{"className":627,"code":628,"language":629,"meta":109,"style":109},"language-sql shiki shiki-themes github-light github-dark","-- Alert query: accounts exceeding $50 in the last hour\nSELECT\n    account_id,\n    SUM(duration_seconds * rate_per_second) AS spend_last_hour,\n    COUNT(*) AS call_count\nFROM cdr\nWHERE call_start > NOW() - INTERVAL '1 hour'\n  AND call_type = 'outbound'\nGROUP BY account_id\nHAVING SUM(duration_seconds * rate_per_second) > 50\nORDER BY spend_last_hour DESC;\n","sql",[38,631,632,637,642,647,652,657,662,667,672,677,682],{"__ignoreMap":109},[165,633,634],{"class":167,"line":168},[165,635,636],{},"-- Alert query: accounts exceeding $50 in the last hour\n",[165,638,639],{"class":167,"line":174},[165,640,641],{},"SELECT\n",[165,643,644],{"class":167,"line":180},[165,645,646],{},"    account_id,\n",[165,648,649],{"class":167,"line":186},[165,650,651],{},"    SUM(duration_seconds * rate_per_second) AS spend_last_hour,\n",[165,653,654],{"class":167,"line":192},[165,655,656],{},"    COUNT(*) AS call_count\n",[165,658,659],{"class":167,"line":198},[165,660,661],{},"FROM cdr\n",[165,663,664],{"class":167,"line":205},[165,665,666],{},"WHERE call_start > NOW() - INTERVAL '1 hour'\n",[165,668,669],{"class":167,"line":251},[165,670,671],{},"  AND call_type = 'outbound'\n",[165,673,674],{"class":167,"line":257},[165,675,676],{},"GROUP BY account_id\n",[165,678,679],{"class":167,"line":263},[165,680,681],{},"HAVING SUM(duration_seconds * rate_per_second) > 50\n",[165,683,684],{"class":167,"line":269},[165,685,686],{},"ORDER BY spend_last_hour DESC;\n",[15,688,689],{},"Run this query every 5 minutes from a monitoring job. When an account crosses the threshold, suspend outbound calling automatically and page the on-call team. The 5-minute detection window limits maximum fraud exposure to roughly $50 × (response time \u002F 5 minutes).",[19,691,693],{"id":692},"security-hardening-checklist","Security Hardening Checklist",[695,696,697,713],"table",{},[698,699,700],"thead",{},[701,702,703,707,710],"tr",{},[704,705,706],"th",{},"Control",[704,708,709],{},"Priority",[704,711,712],{},"Implementation",[714,715,716,728,738,748,759,769,782,792,803,813],"tbody",{},[701,717,718,722,725],{},[719,720,721],"td",{},"Response code normalization",[719,723,724],{},"Critical",[719,726,727],{},"Kamailio\u002FOpenSIPS routing block",[701,729,730,733,735],{},[719,731,732],{},"Rate limiting (pike \u002F ratelimit)",[719,734,724],{},[719,736,737],{},"Load pike module",[701,739,740,743,745],{},[719,741,742],{},"fail2ban on auth failures",[719,744,724],{},[719,746,747],{},"fail2ban + iptables",[701,749,750,753,756],{},[719,751,752],{},"TLS for SIP signaling",[719,754,755],{},"High",[719,757,758],{},"PJSIP TLS transport",[701,760,761,764,766],{},[719,762,763],{},"SRTP for media",[719,765,755],{},[719,767,768],{},"PJSIP + rtpengine SRTP",[701,770,771,774,776],{},[719,772,773],{},"Deny RFC 1918 relay in TURN",[719,775,755],{},[719,777,778,779],{},"coturn ",[38,780,781],{},"denied-peer-ip",[701,783,784,787,789],{},[719,785,786],{},"International dialing restrictions",[719,788,755],{},[719,790,791],{},"Dialplan permission check",[701,793,794,797,800],{},[719,795,796],{},"Per-account call limits",[719,798,799],{},"Medium",[719,801,802],{},"DB-backed call counter",[701,804,805,808,810],{},[719,806,807],{},"Spend velocity alerting",[719,809,799],{},[719,811,812],{},"CDR monitoring query",[701,814,815,818,820],{},[719,816,817],{},"Homer SIPcapture for forensics",[719,819,799],{},[719,821,822],{},"sipcapture module",[15,824,825],{},"A SIP infrastructure that passes this checklist is not unattackable — determined, well-funded attackers still exist. But it is unprofitable to attack opportunistically, which eliminates 95% of the actual threat volume you'll see in production.",[827,828,829],"style",{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":109,"searchDepth":174,"depth":174,"links":831},[832,833,837,838,839,840,841,842,843],{"id":21,"depth":174,"text":22},{"id":83,"depth":174,"text":84,"children":834},[835,836],{"id":98,"depth":180,"text":99},{"id":122,"depth":180,"text":123},{"id":132,"depth":174,"text":133},{"id":152,"depth":174,"text":153},{"id":290,"depth":174,"text":291},{"id":387,"depth":174,"text":388},{"id":467,"depth":174,"text":468},{"id":620,"depth":174,"text":621},{"id":692,"depth":174,"text":693},"SIP","https:\u002F\u002Fimages.unsplash.com\u002Fphoto-1555949963-aa79dcee981c?w=1200&q=80","2025-09-01","Practical SIP security hardening: authentication brute-force prevention, REGISTER flooding mitigation, TLS enforcement, fraud detection patterns, and fail2ban configuration.","md",{},"\u002Fblog\u002Fvoip-security-sip-hardening",{"title":5,"description":847},"blog\u002Fvoip-security-sip-hardening",[854,855,856,857,858,859],"voip-security","sip","hardening","fraud","fail2ban","authentication","ne4Qc017dIvP9S76liXjYw25jhBHhM5KBhKSfY0Wxjk",1776974166862]