> ## Documentation Index
> Fetch the complete documentation index at: https://docs.webacy.com/llms.txt
> Use this file to discover all available pages before exploring further.

# dApp Browser Security

> Secure your dApp browser with URL phishing detection, transaction simulation, and contract verification to block lookalike domains before wallets connect.

A phishing link can look exactly like Uniswap—the domain `uniswaρ.com` uses a Greek rho (ρ) instead of a 'p'. In one attack, 2,400 users connected their wallets before anyone noticed, and \$4.7 million was drained in 24 hours. This guide shows you how to protect your users and prevent this kind of attack.

## Why dApp Browsers Need Security

<CardGroup cols={3}>
  <Card title="URL Protection" icon="link-slash">
    Block phishing sites before users connect their wallets
  </Card>

  <Card title="Transaction Preview" icon="eye">
    Show users what they're signing before they approve
  </Card>

  <Card title="Contract Verification" icon="shield-check">
    Verify the contracts users interact with are safe
  </Card>
</CardGroup>

**Why dApp browser developers choose Webacy:**

* **URL phishing detection** — Catch lookalike domains, homograph attacks, and known scam sites
* **Transaction simulation** — Show users exactly what will happen before signing
* **Contract verification** — Check if the contract is legitimate or malicious
* **Real-time risk scoring** — Fast enough for inline warnings without blocking navigation
* **Multi-chain support** — Protect across all major networks

***

## Prerequisites

Before implementing browser security, ensure you have:

* A Webacy API key ([sign up here](https://developers.webacy.co/billing))
* Basic familiarity with REST APIs or the [Webacy SDK](../sdk/installation)
* Your browser's navigation and transaction signing flows identified

***

## URL Security

The first line of defense is stopping users from connecting to malicious sites.

### Phishing Detection

Check URLs when users navigate to new sites or before they connect their wallet.

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST "https://api.webacy.com/url/check" \
    -H "x-api-key: YOUR_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{"url": "https://uniswap-claim.xyz"}'
  ```

  ```typescript TypeScript theme={null}
  import { ThreatClient } from '@webacy-xyz/sdk';

  const client = new ThreatClient({ apiKey: process.env.WEBACY_API_KEY });

  const result = await client.url.check('https://uniswap-claim.xyz');

  if (result.isPhishing) {
    // Block the connection
    showPhishingWarning({
      url: 'https://uniswap-claim.xyz',
      categories: result.categories,
      message: 'This site has been identified as a phishing site.'
    });
  }

  console.log(`Is phishing: ${result.isPhishing}`);
  console.log(`Is malware: ${result.isMalware}`);
  console.log(`Risk score: ${result.riskScore}`);
  console.log(`Categories: ${result.categories?.join(', ')}`);
  ```

  ```python Python theme={null}
  import requests

  response = requests.post(
      "https://api.webacy.com/url/check",
      headers={
          "x-api-key": "YOUR_API_KEY",
          "Content-Type": "application/json"
      },
      json={"url": "https://uniswap-claim.xyz"}
  )
  result = response.json()
  ```
</CodeGroup>

**Response fields:**

| Field        | Type      | Description                           |
| ------------ | --------- | ------------------------------------- |
| `isPhishing` | boolean   | Known phishing site                   |
| `isMalware`  | boolean   | Known malware distribution            |
| `riskScore`  | number    | 0-100 risk score                      |
| `categories` | string\[] | Risk categories (drainer, scam, etc.) |

### Malware Site Blocking

Block sites that attempt to install malware or exploit browser vulnerabilities.

```typescript theme={null}
async function checkUrlSafety(url: string): Promise<{
  safe: boolean;
  action: 'allow' | 'warn' | 'block';
  reason?: string;
}> {
  const result = await client.url.check(url);

  // Immediate block for known threats
  if (result.isPhishing) {
    return {
      safe: false,
      action: 'block',
      reason: 'This site has been identified as a phishing site.',
    };
  }

  if (result.isMalware) {
    return {
      safe: false,
      action: 'block',
      reason: 'This site distributes malware.',
    };
  }

  // Warn for high risk
  if (result.riskScore > 70) {
    return {
      safe: false,
      action: 'warn',
      reason: `High risk site (score: ${result.riskScore})`,
    };
  }

  // Allow with caution for medium risk
  if (result.riskScore > 40) {
    return {
      safe: true,
      action: 'warn',
      reason: 'Exercise caution with this site.',
    };
  }

  return {
    safe: true,
    action: 'allow',
  };
}
```

### Homograph Attack Detection

<Warning>
  **Homograph attacks** use Unicode characters that look identical to ASCII letters. `аpple.com` (Cyrillic 'а') and `apple.com` (Latin 'a') look the same but are completely different domains.
</Warning>

Common homograph substitutions:

| Real | Fake | Unicode         |
| ---- | ---- | --------------- |
| a    | а    | Cyrillic U+0430 |
| e    | е    | Cyrillic U+0435 |
| o    | о    | Cyrillic U+043E |
| p    | р    | Cyrillic U+0440 |
| c    | с    | Cyrillic U+0441 |
| x    | х    | Cyrillic U+0445 |

The URL check API automatically detects these attacks.

***

## Transaction Security

Every transaction request is an opportunity to protect users.

### Pre-Signing Preview

Simulate transactions before users sign them.

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST "https://api.webacy.com/scan/transaction" \
    -H "x-api-key: YOUR_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "walletAddress": "0xUserAddress...",
      "tx": {
        "from": "0xUserAddress...",
        "to": "0xContractAddress...",
        "data": "0xTransactionData...",
        "value": "0x0"
      },
      "chain": 1,
      "domain": "suspicious-dapp.com"
    }'
  ```

  ```typescript TypeScript theme={null}
  const simulation = await client.scan.scanTransaction(
    '0xUserAddress...',
    {
      tx: {
        from: '0xUserAddress...',
        to: '0xContractAddress...',
        data: '0xTransactionData...',
        value: '0x0',
      },
      chain: 1, // Ethereum mainnet
      domain: currentDappDomain,
    }
  );

  // Show transaction preview to user
  displayTransactionPreview({
    riskLevel: simulation.riskLevel,
    assetChanges: simulation.assetChanges,
    warnings: simulation.warnings,
  });
  ```

  ```python Python theme={null}
  response = requests.post(
      "https://api.webacy.com/scan/transaction",
      headers={
          "x-api-key": "YOUR_API_KEY",
          "Content-Type": "application/json"
      },
      json={
          "walletAddress": "0xUserAddress...",
          "tx": {
              "from": "0xUserAddress...",
              "to": "0xContractAddress...",
              "data": "0xTransactionData...",
              "value": "0x0"
          },
          "chain": 1,
          "domain": "connected-dapp.com"
      }
  )
  simulation = response.json()
  ```
</CodeGroup>

**What to show users:**

| Element          | Priority | Description                            |
| ---------------- | -------- | -------------------------------------- |
| Asset changes    | High     | What tokens/ETH will move              |
| Warnings         | High     | Specific risks detected                |
| Risk level       | Medium   | Overall transaction safety             |
| Contract address | Medium   | What contract they're interacting with |

### Contract Verification

Check if the contract the user is interacting with is legitimate.

<CodeGroup>
  ```bash cURL theme={null}
  curl -X GET "https://api.webacy.com/addresses/0xContractAddress...?chain=eth" \
    -H "x-api-key: YOUR_API_KEY"
  ```

  ```typescript TypeScript theme={null}
  const analysis = await client.addresses.analyze(
    '0xContractAddress...',
    { chain: Chain.ETH }
  );

  // Check if it's a known legitimate contract
  if (analysis.labels?.includes('verified_protocol')) {
    console.log('✓ Verified protocol');
  } else if (analysis.overallRisk > 50) {
    console.warn('⚠️ High risk contract');
  }

  // Check for drainer patterns
  if (analysis.issues?.some(i => i.tag === 'drainer')) {
    console.error('🛑 Known drainer contract detected!');
  }
  ```

  ```python Python theme={null}
  response = requests.get(
      "https://api.webacy.com/addresses/0xContractAddress...",
      params={"chain": "eth"},
      headers={"x-api-key": "YOUR_API_KEY"}
  )
  analysis = response.json()
  ```
</CodeGroup>

***

## Session Protection

Monitor the entire browsing session for threats.

### Connected dApp Monitoring

Track all dApps the user has connected to and periodically recheck their safety.

```typescript theme={null}
interface ConnectedDapp {
  domain: string;
  connectedAt: Date;
  lastChecked: Date;
  riskScore: number;
  status: 'safe' | 'warning' | 'danger';
}

class DappSessionMonitor {
  private connectedDapps: Map<string, ConnectedDapp> = new Map();
  private checkInterval = 5 * 60 * 1000; // 5 minutes

  async onConnect(domain: string): Promise<boolean> {
    const result = await client.url.check(`https://${domain}`);

    if (result.isPhishing || result.isMalware) {
      // Block connection
      return false;
    }

    this.connectedDapps.set(domain, {
      domain,
      connectedAt: new Date(),
      lastChecked: new Date(),
      riskScore: result.riskScore,
      status: this.getStatus(result.riskScore),
    });

    return true;
  }

  async recheckConnectedDapps(): Promise<ConnectedDapp[]> {
    const flagged: ConnectedDapp[] = [];

    for (const [domain, dapp] of this.connectedDapps) {
      // Skip if recently checked
      if (Date.now() - dapp.lastChecked.getTime() < this.checkInterval) {
        continue;
      }

      const result = await client.url.check(`https://${domain}`);

      dapp.lastChecked = new Date();
      dapp.riskScore = result.riskScore;
      dapp.status = this.getStatus(result.riskScore);

      if (result.isPhishing || result.isMalware || result.riskScore > 70) {
        flagged.push(dapp);
      }
    }

    return flagged;
  }

  private getStatus(riskScore: number): 'safe' | 'warning' | 'danger' {
    if (riskScore > 70) return 'danger';
    if (riskScore > 40) return 'warning';
    return 'safe';
  }
}
```

### Risk-Scored Connections

Show users the risk level of their connected dApps.

```typescript theme={null}
async function getConnectionsWithRisk(
  connectedDomains: string[]
): Promise<Array<{
  domain: string;
  riskScore: number;
  status: 'safe' | 'warning' | 'danger';
  recommendation?: string;
}>> {
  const results = await Promise.all(
    connectedDomains.map(async domain => {
      const result = await client.url.check(`https://${domain}`);

      let status: 'safe' | 'warning' | 'danger' = 'safe';
      let recommendation: string | undefined;

      if (result.isPhishing || result.isMalware) {
        status = 'danger';
        recommendation = 'Disconnect immediately';
      } else if (result.riskScore > 70) {
        status = 'danger';
        recommendation = 'Consider disconnecting';
      } else if (result.riskScore > 40) {
        status = 'warning';
        recommendation = 'Exercise caution';
      }

      return {
        domain,
        riskScore: result.riskScore,
        status,
        recommendation,
      };
    })
  );

  return results;
}
```

***

## Complete Integration Workflow

### Navigation Flow

```mermaid theme={null}
flowchart TD
    A[User Navigates to URL] --> B{URL Check}
    B -->|Phishing| C["🛑 BLOCK + Show Warning"]
    B -->|Malware| C
    B -->|Risk > 70| D["⚠️ Show Strong Warning"]
    B -->|Risk 40-70| E["ℹ️ Show Caution Banner"]
    B -->|Risk < 40| F["✅ Allow Navigation"]
    D --> G{User Proceeds?}
    E --> F
    G -->|Yes| F
    G -->|No| H[Return to Safety]
```

### Wallet Connection Flow

```mermaid theme={null}
flowchart TD
    A[dApp Requests Connection] --> B{URL Already Checked?}
    B -->|No| C[Check URL Safety]
    B -->|Yes, Safe| D[Show Connection Request]
    C -->|Safe| D
    C -->|Dangerous| E["🛑 BLOCK Connection"]
    D --> F{User Approves?}
    F -->|Yes| G[Connect Wallet]
    F -->|No| H[Cancel]
    G --> I[Add to Monitored Connections]
```

### Transaction Request Flow

```mermaid theme={null}
flowchart TD
    A[Transaction Request] --> B[Simulate Transaction]
    B --> C{Risk Level?}
    C -->|Critical| D["🛑 BLOCK Transaction"]
    C -->|High| E["⚠️ Strong Warning + Details"]
    C -->|Medium| F["ℹ️ Show Preview + Caution"]
    C -->|Low| G["Show Transaction Preview"]
    E --> H{User Confirms?}
    F --> H
    G --> I[User Signs]
    H -->|Yes| I
    H -->|No| J[Cancel]
    D --> K[Show Why Blocked]
```

### Full TypeScript Implementation

<Accordion title="Complete dApp Browser Security Module">
  ```typescript theme={null}
  import { ThreatClient, Chain } from '@webacy-xyz/sdk';

  const client = new ThreatClient({
    apiKey: process.env.WEBACY_API_KEY,
    defaultChain: Chain.ETH,
  });

  // Types
  type RiskLevel = 'safe' | 'warning' | 'danger' | 'blocked';

  interface UrlCheckResult {
    allowed: boolean;
    riskLevel: RiskLevel;
    message?: string;
    categories?: string[];
  }

  interface TransactionCheckResult {
    allowed: boolean;
    riskLevel: RiskLevel;
    preview: {
      assetChanges: Array<{
        type: string;
        symbol: string;
        amount: string;
      }>;
      warnings: string[];
    };
    requiresConfirmation: boolean;
  }

  interface ConnectionInfo {
    domain: string;
    riskScore: number;
    riskLevel: RiskLevel;
    connectedAt: Date;
    lastTransaction?: Date;
  }

  // URL Security
  async function checkUrlSafety(url: string): Promise<UrlCheckResult> {
    const result = await client.url.check(url);

    // Immediate block
    if (result.isPhishing) {
      return {
        allowed: false,
        riskLevel: 'blocked',
        message: 'This site has been identified as a phishing site. Do not connect your wallet.',
        categories: result.categories,
      };
    }

    if (result.isMalware) {
      return {
        allowed: false,
        riskLevel: 'blocked',
        message: 'This site distributes malware. Navigation blocked for your safety.',
        categories: result.categories,
      };
    }

    // High risk warning
    if (result.riskScore > 70) {
      return {
        allowed: true,
        riskLevel: 'danger',
        message: 'This site has a high risk score. Proceed with extreme caution.',
        categories: result.categories,
      };
    }

    // Medium risk caution
    if (result.riskScore > 40) {
      return {
        allowed: true,
        riskLevel: 'warning',
        message: 'Exercise caution when interacting with this site.',
      };
    }

    return {
      allowed: true,
      riskLevel: 'safe',
    };
  }

  // Transaction Security
  async function checkTransaction(
    userAddress: string,
    txData: { to: string; data: string; value: string },
    chainId: number,
    dappDomain: string
  ): Promise<TransactionCheckResult> {
    // First, verify the contract
    const chain = chainIdToChain(chainId);
    const contractAnalysis = await client.addresses.analyze(txData.to, { chain });

    // Check for known drainers
    const isDrainer = contractAnalysis.issues?.some(i =>
      i.tag.toLowerCase().includes('drainer')
    );

    if (isDrainer) {
      return {
        allowed: false,
        riskLevel: 'blocked',
        preview: {
          assetChanges: [],
          warnings: ['This contract has been identified as a wallet drainer.'],
        },
        requiresConfirmation: false,
      };
    }

    // Simulate the transaction
    const simulation = await client.scan.scanTransaction(userAddress, {
      tx: {
        from: userAddress,
        to: txData.to,
        data: txData.data,
        value: txData.value,
      },
      chain: chainId,
      domain: dappDomain,
    });

    // Determine risk level
    let riskLevel: RiskLevel = 'safe';
    if (simulation.riskLevel === 'critical') {
      riskLevel = 'blocked';
    } else if (simulation.riskLevel === 'high') {
      riskLevel = 'danger';
    } else if (simulation.riskLevel === 'medium') {
      riskLevel = 'warning';
    }

    return {
      allowed: riskLevel !== 'blocked',
      riskLevel,
      preview: {
        assetChanges: simulation.assetChanges ?? [],
        warnings: simulation.warnings ?? [],
      },
      requiresConfirmation: riskLevel === 'danger' || riskLevel === 'warning',
    };
  }

  // Connection Management
  class ConnectionManager {
    private connections: Map<string, ConnectionInfo> = new Map();

    async connect(domain: string): Promise<{
      success: boolean;
      riskLevel: RiskLevel;
      message?: string;
    }> {
      const urlCheck = await checkUrlSafety(`https://${domain}`);

      if (!urlCheck.allowed) {
        return {
          success: false,
          riskLevel: urlCheck.riskLevel,
          message: urlCheck.message,
        };
      }

      const result = await client.url.check(`https://${domain}`);

      this.connections.set(domain, {
        domain,
        riskScore: result.riskScore,
        riskLevel: urlCheck.riskLevel,
        connectedAt: new Date(),
      });

      return {
        success: true,
        riskLevel: urlCheck.riskLevel,
        message: urlCheck.message,
      };
    }

    disconnect(domain: string): void {
      this.connections.delete(domain);
    }

    getConnections(): ConnectionInfo[] {
      return Array.from(this.connections.values());
    }

    async refreshRiskScores(): Promise<ConnectionInfo[]> {
      const flagged: ConnectionInfo[] = [];

      for (const [domain, info] of this.connections) {
        const result = await client.url.check(`https://${domain}`);

        info.riskScore = result.riskScore;

        if (result.isPhishing || result.isMalware) {
          info.riskLevel = 'blocked';
          flagged.push(info);
        } else if (result.riskScore > 70) {
          info.riskLevel = 'danger';
          flagged.push(info);
        } else if (result.riskScore > 40) {
          info.riskLevel = 'warning';
        } else {
          info.riskLevel = 'safe';
        }
      }

      return flagged;
    }
  }

  // Browser Security Controller
  class DappBrowserSecurity {
    private connectionManager = new ConnectionManager();

    // Call when user navigates to a new URL
    async onNavigate(url: string): Promise<UrlCheckResult> {
      return checkUrlSafety(url);
    }

    // Call when dApp requests wallet connection
    async onConnectionRequest(domain: string) {
      return this.connectionManager.connect(domain);
    }

    // Call when dApp requests transaction signature
    async onTransactionRequest(
      userAddress: string,
      txData: { to: string; data: string; value: string },
      chainId: number,
      dappDomain: string
    ) {
      return checkTransaction(userAddress, txData, chainId, dappDomain);
    }

    // Call periodically to check connected dApps
    async refreshConnections() {
      return this.connectionManager.refreshRiskScores();
    }

    // Get all active connections
    getActiveConnections() {
      return this.connectionManager.getConnections();
    }

    // Disconnect a dApp
    disconnect(domain: string) {
      this.connectionManager.disconnect(domain);
    }
  }

  // Helper function
  function chainIdToChain(chainId: number): Chain {
    const mapping: Record<number, Chain> = {
      1: Chain.ETH,
      56: Chain.BSC,
      137: Chain.POL,
      10: Chain.OPT,
      42161: Chain.ARB,
      8453: Chain.BASE,
    };
    return mapping[chainId] ?? Chain.ETH;
  }

  // Export the controller
  export const browserSecurity = new DappBrowserSecurity();
  ```
</Accordion>

***

## Example URLs for Testing

### Known Phishing Sites

Test with URLs that mimic legitimate sites but use different domains:

* `uniswap-claim.com`
* `opensea-claim.xyz`
* `metamask-support.io`

<Warning>
  **Do not actually visit** these example domains—they may be active phishing sites. Use the URL check API to verify them safely.
</Warning>

### Legitimate Sites (for comparison)

| URL                    | Expected Result    |
| ---------------------- | ------------------ |
| `https://uniswap.org`  | Low risk, verified |
| `https://opensea.io`   | Low risk, verified |
| `https://app.aave.com` | Low risk, verified |

### Test Addresses

| Address                                      | Chain | Description                      |
| -------------------------------------------- | ----- | -------------------------------- |
| `0xe7d13137923142a0424771e1778865b88752b3c7` | ETH   | WalletConnect phishing campaign  |
| `0x84672cc56b6dad30cfa5f9751d9ccae6c39e29cd` | ETH   | Permit phishing drainer          |
| `0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045` | ETH   | Vitalik's wallet (safe baseline) |

***

## API Quick Reference

| Endpoint                                 | Use Case               | Response Time |
| ---------------------------------------- | ---------------------- | ------------- |
| `POST /url/check`                        | URL phishing detection | \~200ms       |
| `POST /scan/transaction`                 | Transaction simulation | \~500ms       |
| `GET /addresses/{address}`               | Contract verification  | \~500ms       |
| `GET /addresses/{address}/quick-profile` | Fast risk check        | \~200ms       |

***

## Next Steps

<CardGroup cols={2}>
  <Card title="Get Your API Key" icon="key" href="https://developers.webacy.co/billing">
    Start protecting your browser users
  </Card>

  <Card title="API Reference" icon="book" href="../api-reference/introduction">
    Complete endpoint documentation
  </Card>

  <Card title="Phishing Guide" icon="fish" href="../glossary/phishing">
    Understand phishing attack patterns
  </Card>

  <Card title="Install the SDK" icon="download" href="../sdk/installation">
    TypeScript SDK for integration
  </Card>
</CardGroup>
