[object Object]

You opened up the vendor portal so a hardware supplier could update tickets directly. That was three years ago. The supplier was acquired twice. The original contact left. Their replacement gave the login to a contractor. Somebody, somewhere, still has working credentials and a list of your asset names. This audit takes a day, surfaces what is actually exposed, and produces a remediation list you can defend in front of a security review.

What the vendor portal exposes

A Freshservice vendor portal account is not just a ticket viewer. Depending on configuration it can:

  • See assigned tickets including descriptions and conversation history.
  • See attachments on those tickets.
  • Update status, post notes, attach files.
  • View asset records when relationships are exposed.
  • See requester contact information.

That is plenty of surface for a compromised account. Default-deny posture is the only sane starting point.

The five-question audit

For every vendor account, answer these. Each “no” is an action item.

  1. Identity — is the account a real, currently-employed human at the vendor with a verified email at the vendor’s domain?
  2. Scope — does the account see only tickets and assets it strictly needs?
  3. Expiry — does the account have a defined expiry date, no more than 12 months out?
  4. MFA / SSO — is multi-factor required or is the account on vendor SSO?
  5. Activity — has the account been used in the last 90 days?

The audit is mechanical. Run the queries, build the spreadsheet, walk it row by row.

Pulling the list

curl -s -u "$FS_KEY:X" \
  "https://acme.freshservice.com/api/v2/vendors?per_page=100" \
  | jq '[.vendors[] | {
      id,
      name,
      email,
      created_at,
      last_login_at,
      mfa_enabled,
      sso_user,
      account_status
    }]'

Cross-reference each entry against the vendor’s HR roster. If you do not have a vendor HR roster, your contract owner has a contact list — start there. Anyone in the export who is not on the current contact list is candidate one for deactivation.

Scope tightening

Vendor accounts inherit from a vendor role. Audit the role definition:

  • Which ticket categories can they see? Should be exactly the ones they handle.
  • Can they see attachments by default? If yes, what kind get attached to tickets in their categories? PII screenshots, vendor invoices, internal architecture diagrams?
  • Can they see asset records? Limit to assets explicitly related to their service.
  • Can they comment publicly versus privately?

The default vendor role is broader than most teams realise. Tighten before granting, not after.

{
  "role": "vendor_hw_supplier",
  "permissions": {
    "tickets": {
      "view": "assigned_to_my_company",
      "comment": "public_only",
      "attach_files": true,
      "change_status": ["acknowledged", "in_progress", "awaiting_customer"],
      "delete": false
    },
    "assets": {
      "view": "related_to_assigned_tickets",
      "edit": "specific_fields:firmware_version,serial_number"
    },
    "knowledge_base": {
      "view": "category:hardware_partner_kb"
    }
  }
}

The explicit list is short, defensible, and a security reviewer will not ask follow-up questions on it.

Expiry as policy

Every vendor account should have an expiry date in the future and never further than 12 months out. Six months is better. The renewal process forces a conversation — is this person still at the vendor, is the engagement still active, is the scope still right.

Set the expiry as a custom field on the vendor account. Build a workflow that:

  • Emails the internal contract owner at 60 days before expiry.
  • Emails the vendor contact at 30 days.
  • Auto-deactivates at expiry minus 0.
  • Hard-deletes 90 days post-expiry if nobody re-enabled.
{
  "workflow": "Vendor_Account_Expiry",
  "trigger": "Daily 06:00",
  "queries": [
    {
      "name": "expiring_60",
      "filter": "vendor.expiry_at - now() between 59 and 60 days",
      "action": "notify_internal_owner"
    },
    {
      "name": "expiring_30",
      "filter": "vendor.expiry_at - now() between 29 and 30 days",
      "action": "notify_vendor_and_owner"
    },
    {
      "name": "expired_today",
      "filter": "vendor.expiry_at - now() between 0 and 1 days",
      "action": "deactivate_account"
    }
  ]
}

Mechanical, scheduled, and the only way the cleanup happens without someone remembering.

MFA or SSO

Vendor accounts without MFA are the easiest credential to phish in your entire tenant. The vendor’s employees are not on your security training. Their email hygiene is a function of someone else’s IT department.

Two acceptable postures:

  • Require MFA on every vendor account, enforced at the Freshservice level.
  • Require the vendor to authenticate via their corporate SSO (federated). Their employer’s MFA policy applies. Their employer’s offboarding cuts access immediately.

The second is better when feasible. See freshworks-sso-saml-setup-pitfalls for the SSO config that actually federates correctly.

Attachment policy

Attachments are where leakage happens. Internal users casually upload screenshots that contain other ticket numbers, dashboard previews, customer names. Vendor accounts on those tickets see all of it.

Two mitigations:

  • Train internal users that vendor-visible tickets are external — same hygiene as a customer-facing reply.
  • Build a server-side scan on attachment upload to vendor-scoped tickets that flags images with detected text patterns (email addresses, ticket IDs, credit card-style numbers) for review before they become visible.

The second is harder but pays off the first time you catch a screenshot of a different customer’s PII before it leaks to a contractor.

The log nobody reads

Freshservice logs vendor activity — logins, ticket views, status changes. Almost nobody reads them. They are the first artefact you will want after an incident.

Build a weekly digest:

  • New vendor account creations in the last 7 days. Should match a ticket-tracked request.
  • Logins from new IPs (vendor’s office IPs are roughly stable).
  • Bulk activity (e.g. one account viewing 50+ tickets in an hour).
  • Failed login attempts above a threshold.

Email to security ops or post in a low-volume Slack channel. The digest is uneventful 99% of the time, and the 1% is when you wanted it.

Tie-in to vendor management

The audit feeds the broader vendor management process. A vendor whose contract is expiring should have their portal account expiring at the same time. A vendor in financial distress is a vendor whose offboarding plan should be drafted now, not when the email arrives.

See freshservice-vendor-management for the contract and lifecycle layer this audit complements.

Quarterly cadence

Run the full audit quarterly. Off-cycle, three lighter checks monthly:

  • Pull the inactive list (90 days no login). Deactivate or document why.
  • Pull the expiry list (next 60 days). Confirm or extend.
  • Pull the role membership delta. Anyone added without a ticket request is investigated.

Twenty minutes a month, one day a quarter, and you have a defensible vendor access posture.

Bottom line

Vendor portals decay quietly and breach loudly. Audit identity, scope, expiry, authentication, and activity. Encode expiry as policy, federate authentication where possible, monitor logs you would otherwise ignore. The vendor access program nobody worries about is the one that ran this audit last quarter and the one before.

[object Object]
Share