EdgeOne Logo
Tencent EdgeOne Blog
Tutorial

Tencent EdgeOne Edge Function: How to Implement Anti-theft and Access Control

Tencent EdgeOne - Product Manager

mask.png

Currently, major mainstream vendors have launched their own edge serverless services, such as CloudFlare Workers, Vercel EdgeRuntime, etc. Tencent EdgeOne edge function provides a serverless code execution environment deployed on edge nodes. By writing business function code and setting trigger rules, the code can be flexibly and securely executed on edge nodes close to users.

Before reading this article, please first understand the following background knowledge: Tencent EdgeOne Edge Function.

Why Edge Functions

Access control solutions based on edge functions have high customization and strong security. While ensuring acceleration effects, they can maximize the security of resources and reduce bandwidth and storage costs, effectively saving business costs.

f696b73c-3101-4922-a009-dcdd63a7fc6e.png

In most cases, content distributed through CDN is considered public resources by default, and any user who obtains the URL can access it. However, sometimes protected resources are also accelerated using CDN, and access control at the CDN layer becomes necessary:

  1. Content protection: For paid content on a website, protection is needed to prevent unauthorized access;
  2. Preventing hotlinking: Some websites may directly use someone else's acceleration link to provide content, thereby avoiding storage and bandwidth costs. Access control on resources can effectively prevent hotlinking behavior;
  3. Cost savings: If content is accessed by a large number of unauthorized users, bandwidth and storage costs may increase significantly. Access control can limit access and effectively save costs;
  4. Improving security: By preventing malicious users from attacking the website, CDN access control can improve the overall security of the website.

CDN access control can protect specific resources, so most CDN vendors provide access control functions. However, currently, most CDN vendors' access control capabilities are relatively simple and cannot be customized, so they cannot fully meet complex business scenarios. For customers with higher security needs, these general access control solutions are somewhat unsatisfactory and they have to implement their own access control at the origin server or gateway level.

Access Control Based on Edge Functions

The one-stop edge security acceleration platform EdgeOne is known as the next-generation CDN. Among them, EdgeOne edge function provides an edge code execution environment and supports developers to write and deploy JavaScript code.

Thanks to the programmability of EdgeOne edge function, it is possible to customize exclusive edge access control solutions, which are more convenient, flexible, and secure.Access control is completed at the EdgeOne edge node without the need for origin-pull, maximizing the protection of content, saving resources, and protecting the origin server.

Access control effects:

82cee4b5-d2b2-4146-996c-1d0853c3b4da.png

Code Example

Common access control methods include remote authentication, UA black and white lists, Referer anti-hotlinking, etc. A considerable number of CDN users have customized requirements based on these conventional access control solutions, such as adding or modifying request headers during remote authentication.

Using EdgeOne edge function can easily implement these solutions and provide further customization capabilities.

1. Remote Authentication

Remote authentication refers to forwarding user requests to a designated authentication server, and the authentication server verifies the user requests. If the verification passes, access is allowed. If the verification fails, access is denied or other restrictions are applied, effectively preventing unauthorized users from accessing resources. 

Using edge functions to implement remote authentication:

const AUTH_URL = "...AUTH_URL...";

async function handleRequest(request) {
  const checkAuthRes = await fetch(AUTH_URL, {
    // 可自定义头部
    headers: request.headers,
  });
  if (checkAuthRes.status === 200) {
    return await fetch(request);
  }
  return new Response(null, {
    status: checkAuthRes.status,
  });
}

addEventListener("fetch", (e) => {
  e.respondWith(handleRequest(e.request));
});

2. UA Whitelist

Restricting requests carrying specific User-Agent can effectively prevent access by malicious crawlers or malware. At the same time, customized content can be provided based on different UA headers, for example, only allowing access from mobile devices or specific versions of browsers.

const ALLOWED_UA = ["Mobile/15E148"];

async function handleRequest(request) {
  const userAgent = request.headers.get("User-Agent") || "";

  if (ALLOWED_UA.some((allowedUA) => userAgent.includes(allowedUA))) {
    return await fetch(request);
  } else {
    return new Response(null, { status: 403 });
  }
}

addEventListener("fetch", (event) => {
  event.respondWith(handleRequest(event.request));
});

3. Referer Anti-hotlinking

Anti-hotlinking technology is mainly used to prevent other websites from using your website resources, such as images, videos, documents, etc., without permission.

Checking the Referer field in the HTTP request header is the most common anti-hotlinking method. When the browser sends a request to obtain resources on the web page, it carries the Referer field in the request header, which indicates which page the request is from. By checking the Referer field, the server can determine whether the request comes from a legitimate source.

Implementing Referer anti-hotlinking using edge functions:

const RE_URL = "...URL...";

async function handleRequest(request) {
  const referer = request.headers.get("Referer");

  if (!referer) {
    return Response.redirect(RE_URL);
  }

  const urlInfo = new URL(request.url);
  const isHostMatch = new RegExp(`^https?:\/\/${urlInfo.hostname}`).test(
    referer
  );

  if (!isHostMatch) {
    return Response.redirect(RE_URL);
  }

  return await fetch(request);
}

addEventListener("fetch", (event) => {
  event.respondWith(handleRequest(event.request));
});

4. URL Authentication

URL authentication verifies the encrypted string and timestamp in the URL to protect the origin server resources. There are four types of URL authentication methods: Type A/B/C/D, mainly different in the signature calculation method and the location of the signature.

Implementing Type A authentication using edge functions:

const SECRET_KEY = "YOUR_SECRET_KEY";

async function handleRequest(request) {
  const url = new URL(request.url);
  const params = url.searchParams;
  const authKey = params.get("auth_key");

  if (!authKey) {
    return new Response(null, { status: 401 });
  }

  const [timestamp, nonce, signature] = authKey.split("-");
  const now = Math.floor(Date.now() / 1000);

  if (now > timestamp) {
    return new Response(null, { status: 401 });
  }

  const encoder = new TextEncoder();
  const data = encoder.encode(
    `${url.pathname}-${timestamp}-${nonce}-${SECRET_KEY}`
  );
  const hashBuffer = await crypto.subtle.digest("MD5", data);
  const hashArray = Array.from(new Uint8Array(hashBuffer));
  const expectedSignature = hashArray
    .map((b) => b.toString(16).padStart(2, "0"))
    .join("");

  if (signature !== expectedSignature) {
    return new Response(null, { status: 401 });
  }

  return await fetch(request);
}

addEventListener("fetch", (event) => {
  event.respondWith(handleRequest(event.request));
});

The authentication URL generated by the server is: .../TencentCloud.svg?auth_key=1698224616-0-311b8888ccc20768895f1f3bd82dbb76

The client initiates a request: https://.../TencentCloud.svg?auth_key=1698224616-0-311b8888ccc20768895f1f3bd82dbb76

Trigger the edge function to perform verification on auth_key. Access will be blocked if the signature is incorrect or expired.

cdc978ba-d174-41bc-a7cd-40d74491a769.png

5. Signed Cookies

Related Links:

Using EdgeOne edge function not only allows customization of conventional access control solutions but also enables deployment of more complex exclusive solutions, as demonstrated by the Signed Cookies solution.

The Signed Cookies solution uses asymmetric encryption for access control (private key signature, public key verification). This solution allows secure access permissions to be provided for multiple files without changing each URL.

The working principle of Signed Cookies is: before the user requests access to protected content, the application or website uses a private key to generate a set of special HTTP Cookies (EO-Signature, EO-Policy) that contain access permission information, encrypted and signed. These Cookies are then sent to the user's browser, and the user's browser automatically sends these Cookies in subsequent requests.

When the edge function processes a request with Signed Cookies, it verifies the signature of these Cookies and then decides whether to allow access to the requested content based on the information in the Cookies.

EO-Signature and EO-Policy are generated at the origin server. EO-Policy represents the access control rules and is generated by the server by url-safe base64 encoding the policy.json data. The policy.json data is in the following format:

{
  "Statement": [
    {
      "Resource": "/TencentCloud.svg",
      "Condition": {
        "DateGreaterThan": {
          "EpochTime": 1698318316
        },
        "DateLessThan": {
          "EpochTime": 1698404716
        }
      }
    }
  ]
}

 

EO-Signature stores the signature information, which is signed by the server using the private key for the policy.json data, and then url-safe base64 encoded. After EO-Signature and EO-Policy are planted, all requests for restricted resources on the site will carry these two special HTTP Cookies, as follows:

  • EO-Policy=eyJTdGF0ZW1lbnQiOlt7IlJlc291cmNlIjoiL1RlbmNlbnRDbG91ZC5zdmciLCJDb25kaXRpb24iOnsiRGF0ZUdyZWF0ZXJUaGFuIjp7IkVwb2NoVGltZSI6MTY5ODMxODMxNn0sIkRhdGVMZXNzVGhhbiI6eyJFcG9jaFRpbWUiOjE2OTg0MDQ3MTZ9fX1dfQ__; Path=/;
  • EO-Signature=HddKZBb2Rc7C3Vpat2QnD2rMqnGAjktNbJ9jON2sh1r0o3YX3ohRkvDowdp2PkhBx1erUQbyiL2T6gSW-E5vbYQfrOPC2aA-7a9ZtnkImDZlk-BVfEL1cZgq3bjsG9FxIy-kc0B5JgHtQvRsecEyie0yRyZJZoiMoWxN~haP3ykimFa4kl49eIwtOxCnXTvclHea4Rb1jp7iqTz2W-LevjIeSsqe9dL2qehMSamy-wbOCUTZBSqeoB7dS1hMH4SMSIaBUieBFqs-yrDCtA-E4nNd6FjHoMQi4DJcIkSJiwZl371OJdYyRDX65pWHaUHwbr3GrC4XgpyGWwvMhmaw__; Path=/;

Using edge functions to verify Signed Cookies at the edge node:

const PUBLIC_KEY_PEM = `-----BEGIN PUBLIC KEY-----
...
-----END PUBLIC KEY-----`;

...

async function verifySignature(request) {
  ...
  const eoSignature = getCookieValue(cookies, 'EO-Signature');
  const eoPolicy = getCookieValue(cookies, 'EO-Policy');

  ...

  const signatureBuffer = getSignatureBuff(eoSignature);

  const policyString = getPolicyStr(eoPolicy);
  const policyBuffer = stringToArrayBuffer(policyString);

  const publicKeyBuffer = pemToArrayBuffer(PUBLIC_KEY_PEM);

  const publicKey = await crypto.subtle.importKey(
    'spki',
    publicKeyBuffer,
    {
      name: 'RSASSA-PKCS1-v1_5',
      hash: 'SHA-1',
    },
    true,
    ['verify'],
  );

  const signatureValid = await crypto.subtle.verify('RSASSA-PKCS1-v1_5', publicKey, signatureBuffer, policyBuffer);

  if (!signatureValid) {
    return null;
  }

  return JSON.parse(policyString);
}

function verifyPolicy(request, policy) {
  const { pathname } = new URL(request.url);
  const { Statement } = policy;

  const now = Math.floor(Date.now() / 1000);

  for (const statement of Statement) {
    if (statement.Resource === pathname) {
      const dateGreaterThan = statement.Condition.DateGreaterThan.EpochTime;
      const dateLessThan = statement.Condition.DateLessThan.EpochTime;

      if (now > dateGreaterThan &amp;&amp; now < dateLessThan) {
        return true;
      }
      return false;
    }
  }

  return true;
}

async function handleEvent(event) {
  const { request } = event;

  const policy = await verifySignature(request);
  if (!policy) {
    return event.respondWith(new Response(null, { status: 401 }));
  }
  const valid = verifyPolicy(request, policy);
  if (!valid) {
    return event.respondWith(new Response(null, { status: 403 }));
  }

  event.respondWith(await fetch(request));
}

addEventListener('fetch', handleEvent);

Signed Cookies verification failed, resource access will be blocked:

80ed63bc-d733-4082-9c7a-13cc0f658b66.png

Conclusion

Tencent EdgeOne Edge Functions possess advantages such as distributed deployment, proximity to users, ultra-low latency, elastic scaling, and Serverless environment.

With EdgeOne Edge Functions, developers can customize access control solutions at edge nodes to meet more complex business needs, protect their own resources, reduce bandwidth and storage costs, and effectively save costs. You are welcome to Contact Us for more information.

Edge Function
Develop
Web