Social Engineering & Phishing

Cyber Security · Lesson 22 of 31 7 min read Updated

The easiest way into a company is often not through its firewall but through its people. Social engineering means manipulating someone into doing something that helps an attacker: clicking a link, sharing an OTP, approving a payment or holding a door open. It works because it targets normal human instincts such as helpfulness, fear and respect for authority.

In this lesson you will learn the common social engineering attacks, including scams that are widespread in India, the psychological tricks behind them, how to spot red flags in links and email headers, and the technical and process defences that protect people. This lesson is about recognising and defending against these attacks, never about running them.

The psychology behind it

Almost every scam pulls one or more of these levers:

  • Urgency: "Your account will be blocked in 2 hours."
  • Authority: a message that appears to come from the CEO, a bank, the police or a government department.
  • Fear: "A case has been registered against your Aadhaar number."
  • Greed or reward: "You have won a cashback of ₹5,000", "Earn ₹3,000 a day liking videos."
  • Curiosity: "Salary revision list 2026.xlsx".
  • Helpfulness: a caller from "IT support" who needs your password to fix a problem.

Training people to notice the feeling of pressure ("why am I being rushed?") is one of the most effective defences.

Types of social engineering

AttackChannelTypical example
PhishingEmailFake bank or courier email with a login link
Spear phishingEmail, targetedA message to Meera in accounts mentioning her real manager and a real project
WhalingEmail to senior leadersFake legal notice sent to a company director
Business email compromise (BEC)Email from a real or look-alike account"Our bank details have changed, please pay this invoice to the new account"
SmishingSMS"Your electricity will be disconnected tonight, call this number"
VishingVoice callFake bank officer asking for the OTP "to stop a fraudulent transaction"
QuishingQR codesA sticker over a shop's real QR code, or a QR in an email leading to a fake login
PretextingAnyA made-up story, such as a "vendor audit", to collect information
BaitingPhysicalA USB drive labelled "Exam papers" left in a college lab
TailgatingPhysicalFollowing an employee through a secure door

Scams frequently reported in India include fake KYC-update messages, UPI "collect requests" (you never need to enter your UPI PIN to receive money), fake customer care numbers found through search results, part-time "task" job scams on messaging apps, and "digital arrest" calls where criminals pose as police or investigators on a video call and demand money. There is no procedure called "digital arrest" in Indian law; real agencies do not arrest or settle cases over video calls.

Attackers register domains that look like real ones: extra words (sbi-kyc-update.example), swapped letters, different top-level domains, or homoglyphs, characters from other alphabets that look identical. Browsers show such internationalised domains in an encoded form starting with xn-- (called punycode). This checker looks for common warning signs:

PythonRun it yourself
import ipaddress
from urllib.parse import urlsplit

TRUSTED = {"onlinesbi.sbi", "incometax.gov.in", "shop.example"}

def red_flags(url):
    flags = []
    parts = urlsplit(url)
    host = (parts.hostname or "").lower()
    if parts.scheme != "https":
        flags.append("not HTTPS")
    if "@" in parts.netloc:
        flags.append("'@' in address hides the real host")
    try:
        ipaddress.ip_address(host)
        flags.append("raw IP address instead of a name")
    except ValueError:
        pass
    if any(label.startswith("xn--") for label in host.split(".")):
        flags.append("punycode: looks like " + host.encode().decode("idna"))
    if host not in TRUSTED and any(t.split(".")[0] in host for t in TRUSTED):
        flags.append("brand name inside an untrusted domain")
    if host.count(".") >= 4:
        flags.append("many subdomains")
    return flags or ["no obvious red flags (still verify the sender)"]

for url in ["https://onlinesbi.sbi/login",
            "http://onlinesbi.kyc-verify.example.in/update",
            "https://incometax.gov.in@203.0.113.9/refund",
            "https://xn--shp-ted.example/offers"]:
    print(url)
    for f in red_flags(url):
        print("   -", f)
Output
https://onlinesbi.sbi/login
   - no obvious red flags (still verify the sender)
http://onlinesbi.kyc-verify.example.in/update
   - not HTTPS
   - brand name inside an untrusted domain
https://incometax.gov.in@203.0.113.9/refund
   - '@' in address hides the real host
   - raw IP address instead of a name
https://xn--shp-ted.example/offers
   - punycode: looks like shоp.example

In the third link, everything before the @ is treated as a user name, so the browser actually goes to 203.0.113.9. In the last one, the "o" in shоp is a Cyrillic letter, so the domain only looks like shop.example. No checker is perfect: the safest habit is to never log in through a link in a message, and instead type the address or use the official app.

Email authentication: SPF, DKIM and DMARC

The From: address in an email is just text and is easy to forge. Three DNS-based standards let receiving servers check whether a message really came from the domain it claims:

  • SPF: a TXT record listing the servers allowed to send mail for the domain.
  • DKIM: the sending server signs each message; the public key is published in DNS.
  • DMARC: a TXT record at _dmarc.yourdomain telling receivers what to do when SPF or DKIM checks fail and the domain does not align: p=none (only report), p=quarantine (spam folder) or p=reject (refuse). It also asks for reports, so you learn who is sending mail as you.

Check your own domain's records:

Terminal
dig +short TXT yourdomain.in
dig +short TXT _dmarc.yourdomain.in

A strong setup ends with a DMARC policy such as v=DMARC1; p=reject; rua=mailto:dmarc-reports@yourdomain.in. Receiving servers record their checks in an Authentication-Results header, which you can read with Python's built-in email module:

PythonRun it yourself
from email import message_from_string

raw = """From: "SBI Alerts" <alerts@sbi-secure-kyc.example>
Reply-To: helpdesk.kyc2026@freemail.example
To: kabir@shop.example
Subject: URGENT: KYC expires today, account will be blocked
Authentication-Results: mx.shop.example; spf=fail smtp.mailfrom=sbi-secure-kyc.example; dkim=none; dmarc=fail header.from=sbi-secure-kyc.example

Dear customer, update your KYC within 2 hours using the link below."""

msg = message_from_string(raw)
results = msg["Authentication-Results"]
checks = {part.split("=")[0].strip(): part.split("=")[1].split()[0] for part in results.split(";")[1:]}

print("Subject:", msg["Subject"])
for name in ["spf", "dkim", "dmarc"]:
    print(f"  {name.upper():<6} {checks.get(name, 'missing')}")
from_domain = msg["From"].split("@")[-1].rstrip(">")
reply_domain = msg["Reply-To"].split("@")[-1]
if from_domain != reply_domain:
    print(f"  Replies go to a different domain: {reply_domain}")
if any(word in msg["Subject"].lower() for word in ["urgent", "blocked", "expires"]):
    print("  Pressure words in the subject line")
Output
Subject: URGENT: KYC expires today, account will be blocked
  SPF    fail
  DKIM   none
  DMARC  fail
  Replies go to a different domain: freemail.example
  Pressure words in the subject line

Note that a phishing domain can pass SPF, DKIM and DMARC for its own look-alike domain. These checks prove where mail came from, not that the sender is honest.

Process defences

Technology cannot stop every trick, so organisations add simple rules:

  • Call-back verification: any request to change bank details or make an urgent payment is confirmed by calling a number already on file, never a number in the email.
  • Two-person approval for large payments.
  • Never share OTPs or passwords, with anyone, including people claiming to be from IT or the bank.
  • Phishing-resistant MFA (passkeys, security keys) so stolen passwords and OTPs are useless.
  • Easy reporting: a "Report phishing" button and a team that thanks people for reporting, even false alarms.
  • Awareness training and phishing simulations, run only by the organisation itself, with management approval and care for staff dignity.

As a citizen, report cyber fraud at cybercrime.gov.in or call 1930, and report suspected fraud calls and messages through the Chakshu facility on the government's Sanchar Saathi portal.

Common mistakes

  • Trusting the display name. "SBI Alerts" can be typed by anyone; look at the actual address and domain.
  • Clicking links in messages to log in. Type the address or use the official app instead.
  • Believing the padlock. Phishing sites use HTTPS too.
  • Blaming people who fall for phishing. Shame stops reporting; fast reporting limits damage.
  • Leaving DMARC at p=none for ever. Move to quarantine and then reject once legitimate senders are covered.

Key points

  • Social engineering exploits urgency, authority, fear, greed, curiosity and helpfulness.
  • Know the forms: phishing, spear phishing, BEC, smishing, vishing, quishing, pretexting, baiting and tailgating.
  • Look-alike domains, @ tricks, raw IPs and punycode are common red flags in links.
  • SPF, DKIM and DMARC (ideally p=reject) make domain spoofing much harder.
  • Call-back verification, never sharing OTPs, phishing-resistant MFA and easy reporting are key defences.
  • Report fraud at cybercrime.gov.in or 1930; there is no such thing as a "digital arrest".

Exercise

Add a new rule to red_flags(): flag URLs whose path or query contains words often used in scams, such as kyc, refund, reward or verify. Test it with https://shop.example/account/verify-kyc?reward=5000 and https://shop.example/products.

Show solution
Python
from urllib.parse import urlsplit

SCAM_WORDS = ["kyc", "refund", "reward", "verify"]

def word_flags(url):
    parts = urlsplit(url)
    text = (parts.path + "?" + parts.query).lower()
    found = [w for w in SCAM_WORDS if w in text]
    return f"scam words: {', '.join(found)}" if found else "no scam words"

for url in ["https://shop.example/account/verify-kyc?reward=5000", "https://shop.example/products"]:
    print(url, "->", word_flags(url))

The first URL shows scam words: kyc, reward, verify. On a trusted domain this is a hint, not proof, so combine it with the other checks before deciding.

Quick Quiz

1. An email from your "managing director" asks you to urgently pay a new vendor account. What is the best response?

2. What does a DMARC policy of p=reject tell receiving mail servers?

3. A link reads https://incometax.gov.in@203.0.113.9/refund. Where does it really go?

4. Which statement about "digital arrest" calls is true?

Learn Cyber Security with a mentor

Free lessons get you started. Our Ethical Hacking & Cyber Security training adds real projects and code reviews from working developers.

  • Project-based learning
  • Mentor code reviews
  • Free counselling call