<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Kanika's Tech Notes]]></title><description><![CDATA[Articles, notes, and tutorials on Python programming, automation, and cybersecurity.]]></description><link>https://kanikatiwari.hashnode.dev</link><generator>RSS for Node</generator><lastBuildDate>Wed, 28 Oct 2026 06:39:35 GMT</lastBuildDate><atom:link href="https://kanikatiwari.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Building LogSentry: Automating Brute-Force Threat Detection in Python

]]></title><description><![CDATA[As web servers and applications scale, manual security oversight becomes impossible. If an attacker targets your infrastructure with a credential stuffing…]]></description><link>https://kanikatiwari.hashnode.dev/building-logsentry-automating-brute-force-threat-detection-in-python</link><guid isPermaLink="true">https://kanikatiwari.hashnode.dev/building-logsentry-automating-brute-force-threat-detection-in-python</guid><category><![CDATA[Python]]></category><category><![CDATA[cybersecurity]]></category><category><![CDATA[automation]]></category><category><![CDATA[Programming Blogs]]></category><category><![CDATA[softwareengineering #security]]></category><dc:creator><![CDATA[KANIKAkt]]></dc:creator><pubDate>Mon, 26 Oct 2026 23:03:52 GMT</pubDate><content:encoded><![CDATA[<p>As web servers and applications scale, manual security oversight becomes impossible. If an attacker targets your infrastructure with a credential stuffing or brute-force attack, waiting for manual log reviews means the breach has already happened.</p>
<p>System administrators and developers need automated, lightweight tools that parse server logs, isolate anomalous behavior, and flag threats instantly—without requiring bloated enterprise SIEM software.</p>
<p>In this guide, we will break down how I built <strong>LogSentry</strong>, a custom Python security utility that parses authentication logs using regular expressions, tracks failed login attempts via hash-mapped collections, and flags potential brute-force risks right from the command line.</p>
<hr />
<h2>The Technical Architecture</h2>
<p>To build a reliable log analyzer from scratch without third-party dependencies, we rely on three core Python modules:</p>
<ul>
<li><code>re</code>: For precise pattern matching to extract timestamps, usernames, and source IP addresses from raw log files.</li>
<li><code>collections.defaultdict</code>: For clean, efficient grouping of failed attempt counts per IP address.</li>
<li><code>argparse</code>: To build a professional command-line interface (CLI) allowing customizable log paths and threshold parameters.</li>
</ul>
<hr />
<h2>Step 1: Writing the Regex Parser &amp; Threat Engine (<code>logsentry.py</code>)</h2>
<p>The heart of the utility is a regex pattern designed to capture standard authentication log formats (such as SSH <code>auth.log</code>). It scans the file line-by-line to ensure memory efficiency even when processing massive log archives.</p>
<pre><code class="language-python">import re
from collections import defaultdict
import argparse

def parse_auth_logs(file_path, threshold=3):
    """
    Parses an authentication log file, extracts IP addresses and failed attempts,
    and flags potential brute-force activities exceeding the threshold.
    """
    failure_pattern = re.compile(
        r"([A-Z][a-z]{2}\s+\d+\s+\d{2}:\d{2}:\d{2}).*Failed password for (?:invalid user )?(\S+) from (\d{1,3}(?:\.\d{1,3}){3})"
    )
    
    failed_attempts = defaultdict(list)
    total_parsed = 0

    try:
        with open(file_path, "r", encoding="utf-8") as f:
            for line in f:
                total_parsed += 1
                match = failure_pattern.search(line)
                if match:
                    timestamp_str, user, ip_address = match.groups()
                    failed_attempts[ip_address].append({
                        "user": user,
                        "timestamp": timestamp_str
                    })
    except FileNotFoundError:
        print(f"[-] Error: The log file '{file_path}' was not found.")
        return

    print(f"[+] Audit Complete. Parsed {total_parsed} lines.")
    print("-" * 50)
    print(f"{'IP ADDRESS':&lt;18} | {'FAILED COUNT':&lt;12} | {'STATUS'}")
    print("-" * 50)

    for ip, attempts in failed_attempts.items():
        count = len(attempts)
        status = "[!] THREAT DETECTED (Brute-Force Risk)" if count &gt;= threshold else "[i] Normal"
        print(f"{ip:&lt;18} | {count:&lt;12} | {status}")

if __name__ == "__main__":
    parser = argparse.ArgumentParser(description="LogSentry - Local Log Analyzer &amp; Threat Detector")
    parser.add_argument("--logfile", type=str, default="auth_sample.log", help="Path to the log file")
    parser.add_argument("--threshold", type=int, default=2, help="Failed attempt threshold for threat flagging")
    
    args = parser.parse_args()
    parse_auth_logs(args.logfile, threshold=args.threshold)
</code></pre>
<hr />
<h2>Step 2: Testing with Mock Log Data</h2>
<p>To verify the utility, we can set up a sample log file named <code>auth_sample.log</code> containing simulated authentication entries:</p>
<pre><code class="language-text">Oct 24 01:10:12 server1 sshd[4120]: Accepted publickey for ubuntu from 10.0.0.5 port 22 ssh2
Oct 24 01:12:05 server1 sshd[4215]: Failed password for root from 192.168.1.50 port 38210
Oct 24 01:12:08 server1 sshd[4217]: Failed password for root from 192.168.1.50 port 38212
Oct 24 01:12:12 server1 sshd[4219]: Failed password for admin from 192.168.1.50 port 38214
Oct 24 01:15:30 server1 sshd[4301]: Accepted publickey for deploy from 10.0.0.8 port 22 ssh2
Oct 24 01:18:44 server1 sshd[4410]: Failed password for invalid user test from 45.33.32.156 port 51220
Oct 24 01:18:46 server1 sshd[4412]: Failed password for invalid user guest from 45.33.32.156 port 51222
</code></pre>
<p>When running the script from the terminal with a threshold of 3:</p>
<pre><code class="language-bash">python logsentry.py --logfile auth_sample.log --threshold 3
</code></pre>
<p>The terminal outputs a clean, actionable audit report:</p>
<pre><code class="language-text">[+] Audit Complete. Parsed 7 lines.
--------------------------------------------------
IP ADDRESS         | FAILED COUNT | STATUS
--------------------------------------------------
192.168.1.50       | 3            | [!] THREAT DETECTED (Brute-Force Risk)
45.33.32.156       | 2            | [i] Normal
</code></pre>
<hr />
<h2>Key Takeaway</h2>
<p>Automating log analysis protects infrastructure against fast-moving automated threats without adding heavy software overhead. Building custom Python utilities gives developers complete control over how logs are parsed, filtered, and alerted.</p>
<p>Check out the full repository on <a href="https://github.com/KANIKAkt">GitHub</a> to view the source code and documentation.</p>
<p>👉 <strong>Need a custom Python automation script or security utility built for your infrastructure?</strong> Connect with me via my <a href="https://www.fiverr.com/s/433aDm1">Fiverr Services</a> profile!</p>
<p>#python #cybersecurity #automation #programming #softwareengineering #security</p>
<hr />
]]></content:encoded></item><item><title><![CDATA[Detecting File Tampering in Python Using SHA-256 Cryptographic Hashing]]></title><description><![CDATA[How do security engineers, forensic investigators, and software distributors know if a sensitive file has been secretly modified by an attacker? They don't…]]></description><link>https://kanikatiwari.hashnode.dev/detecting-file-tampering-in-python-using-sha-256-cryptographic-hashing</link><guid isPermaLink="true">https://kanikatiwari.hashnode.dev/detecting-file-tampering-in-python-using-sha-256-cryptographic-hashing</guid><category><![CDATA[Python]]></category><category><![CDATA[cybersecurity]]></category><category><![CDATA[Cryptography]]></category><category><![CDATA[Programming Blogs]]></category><dc:creator><![CDATA[KANIKAkt]]></dc:creator><pubDate>Fri, 23 Oct 2026 20:37:32 GMT</pubDate><content:encoded><![CDATA[<p>How do security engineers, forensic investigators, and software distributors know if a sensitive file has been secretly modified by an attacker? They don't compare files line by line—they use <strong>cryptographic hashing</strong>.</p>
<p>A hash function takes any file (whether it is a 1 KB text file or a 10 GB operating system image) and generates a unique, fixed-length string of characters called a <strong>digital fingerprint</strong> or <strong>checksum</strong>. </p>
<p>In this guide, we will build a File Integrity Checker in Python using the built-in <code>hashlib</code> library and demonstrate how <strong>SHA-256</strong> instantly detects unauthorized file modifications.</p>
<hr />
<h3>Prerequisites</h3>
<p>You do not need to install any third-party packages for this project. Both <code>hashlib</code> and <code>os</code> come pre-installed with <strong>Python 3.x</strong>.</p>
<hr />
<h3>Step 1: Why Read Files in Chunks?</h3>
<p>A common beginner mistake when hashing files in Python is calling <code>file.read()</code> all at once. If you try to hash a 4 GB video file or memory dump that way, Python will attempt to load all 4 GB into your system RAM at once, crashing your script.</p>
<p>Instead, we read the file in <strong>64 KB binary chunks</strong> (<code>65536</code> bytes) and feed each chunk into the SHA-256 algorithm sequentially. This keeps memory usage near zero regardless of file size.</p>
<hr />
<h3>Step 2: Writing the Integrity Checker (<code>file_integrity.py</code>)</h3>
<p>Create a file named <code>file_integrity.py</code> and add the following implementation:</p>
<pre><code class="language-python">import hashlib
import os

def calculate_sha256(filepath):
    """Calculate SHA-256 hash by reading in 64KB chunks."""
    sha256 = hashlib.sha256()
    
    if not os.path.exists(filepath):
        print(f"[-] Error: File '{filepath}' not found.")
        return None

    # Read binary mode in chunks to handle large files safely
    with open(filepath, "rb") as f:
        while chunk := f.read(65536):
            sha256.update(chunk)
            
    return sha256.hexdigest()

def demo_integrity():
    test_file = "confidential_contract.txt"

    # 1. Create a simulated official document
    with open(test_file, "w") as f:
        f.write("Payment terms: Transfer 5,000 USD to Contractor A.")

    # 2. Record its baseline fingerprint (Golden Hash)
    baseline_hash = calculate_sha256(test_file)
    print(f"[*] Document created: '{test_file}'")
    print(f"[*] Original Hash   : {baseline_hash}\n")

    # 3. Simulate an unauthorized modification (Tampering)
    print("[!] Simulating unauthorized edit by an attacker...")
    with open(test_file, "w") as f:
        # Attacker modifies '5,000' to '9,000'
        f.write("Payment terms: Transfer 9,000 USD to Contractor A.")

    # 4. Re-calculate the hash
    current_hash = calculate_sha256(test_file)
    print(f"[*] Current Hash    : {current_hash}\n")

    # 5. Integrity Verification Audit
    print("=" * 20 + " VERIFICATION AUDIT " + "=" * 20)
    if current_hash == baseline_hash:
        print("[+] VERIFIED: The file is authentic and unchanged.")
    else:
        print("[-] TAMPER DETECTED: Hashes do not match!")
        print("[-] The contents have been altered or corrupted.")

if __name__ == "__main__":
    demo_integrity()
</code></pre>
<hr />
<h3>Step 3: Running the Verification Audit</h3>
<p>Run the script from your terminal:</p>
<pre><code class="language-bash">python file_integrity.py
</code></pre>
<h4>Terminal Output:</h4>
<pre><code class="language-text">[*] Document created: 'confidential_contract.txt'
[*] Original Hash   : 14e118e3953411eed56a2660dc2aa1d0990fca696d962b946595e6d7a94297fe

[!] Simulating unauthorized edit by an attacker...
[*] Current Hash    : f3f7b6971cb53579d80f0f473baaccd0aeb5e6eba8b83fd9dc718f025b09a4a0

==================== VERIFICATION AUDIT ====================
[-] TAMPER DETECTED: Hashes do not match!
[-] The contents have been altered or corrupted.
</code></pre>
<hr />
<h3>Understanding the "Avalanche Effect"</h3>
<p>Notice how we only altered a single digit in our text file (changing <code>5</code> to <code>9</code>). Yet the output hash didn't just change by one digit—it mutated from <code>14e118...</code> to <code>f3f7b6...</code>.</p>
<p>In cryptography, this is known as the <strong>Avalanche Effect</strong>. A microscopic change in the input causes a drastic, unpredictable change in the output hash, making it virtually impossible for an attacker to modify a file while keeping the same SHA-256 signature.</p>
<hr />
<h3>Key Takeaway</h3>
<p>File Integrity Monitoring (FIM) is a cornerstone of system security and digital forensics. By recording baseline SHA-256 hashes of critical system configuration files or forensic evidence, you can mathematically prove whether data has remained intact or suffered tampering.</p>
]]></content:encoded></item><item><title><![CDATA[How to Extract Hidden EXIF Metadata from Images Using Python]]></title><description><![CDATA[Every time you take a photo with a smartphone or digital camera, the device embeds invisible information called EXIF metadata (Exchangeable Image File…]]></description><link>https://kanikatiwari.hashnode.dev/how-to-extract-hidden-exif-metadata-from-images-using-python</link><guid isPermaLink="true">https://kanikatiwari.hashnode.dev/how-to-extract-hidden-exif-metadata-from-images-using-python</guid><category><![CDATA[Python]]></category><category><![CDATA[cybersecurity]]></category><category><![CDATA[Programming Blogs]]></category><category><![CDATA[beginner developers]]></category><dc:creator><![CDATA[KANIKAkt]]></dc:creator><pubDate>Fri, 23 Oct 2026 20:12:55 GMT</pubDate><content:encoded><![CDATA[<p>Every time you take a photo with a smartphone or digital camera, the device embeds invisible information called <strong>EXIF metadata</strong> (Exchangeable Image File Format).</p>
<p>This metadata quietly records device details:</p>
<ul>
<li>The camera make and model</li>
<li>The exact timestamp of when the image was taken</li>
<li>Camera settings (exposure, focal length, orientation)</li>
<li>Even GPS coordinates pointing to where the photo was captured</li>
</ul>
<p>In this quick guide, we will build a lightweight Python script using the <code>Pillow</code> library to inspect and decode hidden EXIF tags from any image.</p>
<hr />
<h3>Prerequisites</h3>
<p>You only need:</p>
<ul>
<li><strong>Python 3.x</strong> installed on your system</li>
<li>The <strong>Pillow</strong> imaging library</li>
</ul>
<p>Install Pillow via your terminal:</p>
<pre><code class="language-bash">pip install pillow
</code></pre>
<hr />
<h3>Step 1: Understanding Why We Need <code>TAGS</code></h3>
<p>When you query an image for EXIF data in raw Python, the library returns numeric IDs instead of readable names. For example, the camera model might be stored under tag ID <code>272</code>, and the timestamp under <code>306</code>.</p>
<p>To make this human-readable, Pillow provides a dictionary called <code>TAGS</code> that translates these numeric IDs into descriptive strings like <code>"Model"</code> and <code>"DateTime"</code>.</p>
<hr />
<h3>Step 2: Writing the Python Extractor</h3>
<p>Create a file named <code>extractor.py</code> and add the following code:</p>
<pre><code class="language-python">import os
from PIL import Image
from PIL.ExifTags import TAGS

def extract_metadata(image_path):
    print(f"[*] Checking image: {image_path}")
    
    if not os.path.exists(image_path):
        print(f"[-] ERROR: File '{image_path}' not found.")
        return

    try:
        # Load the image
        image = Image.open(image_path)
        exif_data = image.getexif()

        # Fallback for images storing tags in legacy EXIF dictionaries
        if not exif_data:
            legacy_getter = getattr(image, '_getexif', None)
            if callable(legacy_getter):
                exif_data = legacy_getter()

        if not exif_data:
            print("[-] No EXIF metadata found in this image.")
            return

        print(f"\n{'='*20} METADATA FOUND {'='*20}\n")
        
        # Loop through raw tags and convert them to readable names
        for tag_id, value in exif_data.items():
            tag_name = TAGS.get(tag_id, tag_id)
            print(f"{tag_name:&lt;25}: {value}")

    except Exception as e:
        print(f"[-] Failed to process image: {e}")

if __name__ == "__main__":
    extract_metadata("test.jpg")
</code></pre>
<hr />
<h3>Step 3: Running the Script and Inspecting Output</h3>
<p>To test the script, place a camera photograph named <code>test.jpg</code> in your directory and execute:</p>
<pre><code class="language-bash">python extractor.py
</code></pre>
<h4>Terminal Output:</h4>
<pre><code class="language-text">[*] Checking image: test.jpg

==================== METADATA FOUND ====================

GPSInfo                   : 926
ResolutionUnit            : 2
ExifOffset                : 268
Make                      : NIKON
Model                     : COOLPIX P6000
Software                  : Nikon Transfer 1.1 W
Orientation               : 1
DateTime                  : 2008:11:01 21:15:07
YCbCrPositioning          : 1
XResolution               : 300.0
YResolution               : 300.0
</code></pre>
<p>The script reveals that the photo was shot on a <strong>NIKON COOLPIX P6000</strong> on <strong>November 1, 2008</strong>, and also points to embedded GPS data.</p>
<hr />
<h3>Why Did My Personal Photo Show "No EXIF Metadata"?</h3>
<p>If you test this script on photos downloaded from WhatsApp, Instagram, or Telegram, you will likely see <code>[-] No EXIF metadata found</code>.</p>
<p>Major social media and messaging platforms intentionally strip EXIF data upon upload to protect user privacy and prevent strangers from extracting home locations or device signatures.</p>
<hr />
<h3>Key Takeaway</h3>
<p>Reading image headers is a core fundamental of digital forensics, OSINT investigations, and security audits. In just a few lines of Python, you can audit your own image files and verify whether your personal data is exposed before sharing it online.</p>
]]></content:encoded></item></channel></rss>