May 19, 2025

XCMP in Polkadot: Security Measures Every Project Should Know

Have you ever tried to transmit a message from one blockchain to another without employing a ready-made bridge?

Cross-Chain
Substrate
Polkadot
XCMP
A vibrant staircase with orange railings set against a backdrop of urban skyscrapers.

If you have, you’ve likely encountered frustrating challenges like:

  • Half-baked or incomprehensible APIs
  • Poor developer experience and lack of testing tools
  • Verification issues across different consensus mechanisms
  • Mismatched message format interpretations between chains
  • Insufficient permissions to execute certain operations on the receiving chain

For most blockchain ecosystems, such a cross-chain interoperability challenge requires complex bridging solutions that introduce additional security risks and points of failure. However, Polkadot takes a different approach.

Within the Polkadot ecosystem, Cross-Consensus Message Passing (XCMP) and the XCM format provide a standard framework for interoperability between parachains. Yet despite being the native solution designed specifically for this purpose, implementing secure cross-chain communication using XCM remains a complex challenge. Understanding the nuances of Cross-Consensus Messaging (XCM) in Polkadot is crucial for any project looking to leverage the interoperability promises of this ecosystem.

For any project in the Web3 field, addressing cross-chain challenges is critical, as this is precisely where malicious actors can exploit vulnerabilities — not only to steal assets in transit but also, in some cases, to gain control over the remote blockchain. Cross-chain mechanisms can also be used by such actors to conceal assets obtained from on-chain attacks. This is why, at Taran Space, we are deeply committed to ensuring secure blockchain interoperability.

Why This Matters

If you’re building applications on Polkadot or considering launching a parachain, your understanding of XCM is absolutely essential. It’s not only about security — it’s about your project’s fundamental ability to function within the ecosystem. Although it’s not mandatory to implement XCM for operating a basic parachain, it becomes crucial when you seek to enable any substantial functionality. This is primarily because you’ll likely want to facilitate integration with AssetHub or incorporate cross-chain transfers. Our journey through auditing numerous Polkadot applications and XCM implementations reveals important technical trade-offs and security considerations that could save your project from critical vulnerabilities.

What is XCM Exactly?

XCM, or Cross-Consensus Messaging, is the foundation of Polkadot’s interoperability promise. Unlike what many developers initially assume, XCM is not a protocol — it’s a standardized message format designed to facilitate communication between disparate consensus systems.

The protocol responsible for transporting these messages is XCMP (Cross-Chain Message Passing), which operates based on four core principles:

  1. Asynchronous: Messages operate independently of sender acknowledgment
  2. Absolute: Messages are guaranteed accurate delivery and interpretation
  3. Asymmetric: Messages follow a “fire and forget” paradigm with no automatic feedback
  4. Agnostic: XCMP works independently of specific consensus mechanisms

Together, XCM and XCMP create a powerful framework for cross-chain communication in Polkadot. However, this architecture also introduces unique security challenges that developers must proactively address.

The Deceptive Complexity

Despite being Polkadot’s native solution for cross-chain communication, implementing XCM is far from straightforward. Developers often find themselves navigating a maze of complexities when working with XCM. It’s not just about sending a message from one chain to another. It’s about crafting precise instructions that will be interpreted and executed in a completely different runtime environment.

Consider this simplified XCM message for transferring tokens between accounts:

Xcm(vec![
    WithdrawAsset((Here, send_amount).into()),
    BuyExecution {
        fees: (Here, send_amount).into(),
        weight_limit: WeightLimit::Unlimited
    },
    DepositAsset {
        assets: AllCounted(1).into(),
        beneficiary: Parachain(2).into()
    },
]);

Each instruction here is a critical step: withdrawing assets, paying for execution, and depositing to the recipient. A mistake in any part of this sequence can lead to lost funds or security vulnerabilities.

How to Work With XCM?

Working with XCM means crafting a precise sequence of instructions that govern how assets and data move between chains. Here are the foundational steps:

Let’s walk through a basic token transfer using XCM:

Start Simple

Begin with basic token transfers or minimal interactions.

ParaA::execute_with(|| {
    let message = Xcm(vec![
        WithdrawAsset((Here, send_amount).into()),
        BuyExecution {
            fees: (Here, send_amount).into(),
            weight_limit: WeightLimit::Unlimited
        },
        DepositAsset {
            assets: AllCounted(1).into(),
            beneficiary: Parachain(2).into()
        },
    ]);

    // Send withdraw and deposit
    assert_ok!(ParachainPalletXcm::send_xcm(Here, Parent, message.clone()));
});

Test first

Use tools like the XCM simulator, emulator, or dry-run API to validate behavior before deploying on a live network. View the full working example here.

Relay::execute_with(|| {
    assert_eq!(
        relay_chain::Balances::free_balance(child_account_id(1)),
        INITIAL_BALANCE - send_amount
    );
    assert_eq!(
        relay_chain::Balances::free_balance(child_account_id(2)),
        INITIAL_BALANCE + send_amount
    );
});

Expand Gradually

Once confident, move on to advanced features like remote contract execution or query responses.

XCM may appear complex, but by isolating each instruction and testing thoroughly, it becomes manageable. Explore more examples and best practices here.

The Security Danger Zones

Through our security audits of Polkadot projects, we’ve identified several high-risk areas where XCM implementations frequently fail. Being aware of these can help your project avoid common pitfalls.

1. Arbitrary Execution

A poorly configured XCM filter can allow attackers to execute arbitrary instructions on your parachain, potentially leading to unauthorized actions or complete system compromise. This implies that if the XCM filter on your parachain is overly permissive, the security of your parachain hinges not only on your own measures but also on the security protocols of any connected chain within the entire Polkadot ecosystem, or even an external blockchain. For example, should there exist a flawed parachain that permits regular users to craft and dispatch XCM messages on its behalf, it could be exploited as an attack vector against your parachain.

pub struct SafeCallFilter;

impl Contains<RuntimeCall> for SafeCallFilter {
    fn contains(call: &RuntimeCall) -> bool {
        Everything::contains(call)
    }
}

This configuration would enable the execution of any Transact instruction, effectively giving external actors complete control over your chain. For instance, an attacker can send an XCM message containing a Transact instruction that calls critical governance functions, allowing them to drain treasury funds, modify protocol parameters, or even halt chain operations entirely.

Mitigation: Implement strict call filters that whitelist only specific, necessary operations.

pub struct SafeCallFilter;

impl Contains<RuntimeCall> for SafeCallFilter {
    fn contains(call: &RuntimeCall) -> bool {
        matches!(call, RuntimeCall::System(
            pallet_system::Call::remark_with_event { .. })
        )
    }
}

2. Denial of Service (DoS)

Without proper filtering, your parachain can be flooded with malicious XCM messages, causing performance degradation or complete service disruption.

An attacker with access to a vulnerable parachain allowing dispatch of XCM could use the send extrinsic to spam millions of XCMs to other chains. If the receiving chains don’t implement proper filtering or rate limiting, the XCM queues could become cluttered, preventing legitimate messages from being processed.

In severe cases, this can lead to the targeted chain dropping incoming messages entirely, effectively cutting it off from the rest of the Polkadot ecosystem.

Mitigation:

  • Implement rate limiting for incoming messages
  • Process XCMs only from trusted parachains
  • Create barriers that filter messages based on their origin
  • Set up proper queue management to prioritize messages from critical services

3. Storage Exhaustion

If charges for on-chain storage via XCM are set too low, attackers can fill your storage with junk data at minimal cost. An attacker can craft XCM messages that create numerous small storage entries until your chain’s state grows unmanageably large. This is particularly dangerous because storage costs are often underestimated in cross-chain contexts.

Mitigation:

  • Ensure you charge adequate fees for storage
  • Implement caps on the amount of data that can be saved through XCM operations
  • Consider time-based expiration for temporary storage items

4. Weight Miscalculation

Inaccurate weight calculations can lead to resource exhaustion or economic imbalances. If you undercharge for computational resources in XCM operations, an attacker could send complex XCM messages that consume disproportionate amounts of your chain’s resources while paying minimal fees, creating an economic DoS condition.

This principle extends beyond XCM. All extrinsics — especially those exposed to users — must be carefully benchmarked and tested to prevent misuse or overconsumption of resources.

Mitigation:

  • Implement conservative weight estimations that slightly overcharge rather than undercharge
  • Regularly review and adjust weight calculations based on actual runtime performance
  • Consider implementing weight caps for specific XCM operations

5. Cross-Chain Confused Deputy Attacks

Parachain B might trust messages from Parachain A, but a vulnerability in A could allow malicious actors to send messages that appear legitimate.

If Parachain A has vulnerable permissions that allow arbitrary message creation, an attacker can exploit this to send trusted messages to Parachain B, which might execute destructive operations based on this misplaced trust.

Mitigation:

  • Implement origin verification that goes beyond simple parachain ID checks
  • Consider multi-signature requirements for high-value operations
  • Establish trust tiers for different parachains based on their security posture

Practical Security Measures

Based on our experience securing Polkadot projects, here are critical security measures every project should implement:

1. Rigorous XCM Configuration Testing

Use the XCM Emulator to test your XCM configuration before deployment.

decl_test_networks! {
    pub struct TestNet {
        relay_chain = RelayChain,
        parachains = vec![
            ParachainA,
            ParachainB,
        ],
        bridge = ()
    }
}

The emulator allows you to verify that your XCM interactions behave as expected without deploying to a live network.

2. Implement Strict Barriers

XCM barriers are your first line of defense against malicious messages.

type Barrier = (
    TakeWeightCredit,
    AllowTopLevelPaidExecutionFrom<Everything>,
    AllowKnownQueryResponses<PolkadotXcm>,
    AllowSubscriptionsFrom<Everything>,
);

This example barrier allows only specific types of messages while blocking others.

3. Use Dry Run APIs

Before sending actual XCM messages, use the Dry Run API to validate their effects.

fn dry_run_xcm(origin_location: VersionedLocation, xcm: VersionedXcm<Call>) -> Result<XcmDryRunEffects<Event>, Error>;

This helps identify potential issues before they impact live systems.

4. Careful Payment Configuration

Ensure your weight calculations for XCM execution are accurate to prevent both overpayment and underpayment scenarios.

fn query_weight_to_asset_fee(weight: Weight, asset: VersionedAssetId) -> Result<u128, Error>;

This API helps determine appropriate fees for XCM execution.

Real-world Implementation Strategy

When we work with Polkadot projects to secure their XCM implementations, we recommend a phased approach:

  1. Start Restrictive: Begin with the most restrictive configuration possible, allowing only essential operations
  2. Test Thoroughly: Use the XCM Emulator and Dry Run APIs to validate every aspect of your implementation
  3. Expand Gradually: Only add new capabilities after thorough testing and security review
  4. Regular Audits: As XCM continues to evolve, regular security audits are essential to identify new vulnerabilities

Conclusion

XCM is a powerful tool for enabling true interoperability in the Polkadot ecosystem, but its complexity demands careful implementation and rigorous security practices. By understanding the common vulnerabilities and implementing the security measures outlined in this post, your project can leverage XCM’s capabilities while avoiding its pitfalls.

At Taran Space, we’re committed to helping Polkadot projects navigate these challenges. Securing cross-chain messaging isn’t just about protecting assets — it’s about building the foundation for a truly interoperable blockchain future.

If you’re building on Polkadot and want to ensure your XCM implementation is secure, reach out to our team for a comprehensive security audit. The future of blockchain is interconnected, and we’re here to make sure that future is secure.

Want to learn more about securing your Polkadot project? Check out our other articles on blockchain security or contact us for a consultation.