Yes. SPF can be spoofed, but it's not easy.
The only reliable ways to do it are with DNS hijacking (such as a MitM attack, which DNSSEC spoils) or with IP spoofing like BGP hijacking. Both of these are nontrivial.
In other instances, you can take advantage of overly-broad SPF implementations.
Some SPF records bless very large ranges of IP addresses. If you are able to obtain one of those, say because it's been rotated out of the domain's pool at a hosting company, you'd be able to spoof that domain's mail until the SPF record is updated. There's even recent news of an attacker bypassing SPF because that record permitted a
/2 CIDR (1.1 billion addresses) instead of the
/24 CIDR they probably intended (256 addresses).
When SPF employs the
ptr mechanism, it says that any network claiming to match passes SPF*. This is problematic because any network operator can choose whatever they like as the PTR record for an IP they control.
(*Technically, the SPF spec requires verifying that PTR records actually point back to the same IP, aka FCrDNS. Because FCrDNS requires more DNS lookups and therefore more latency, RFC 7208 explicitly says "Use of the
ptr mechanism and the
%p macro has been strongly discouraged".)
Another way to forge mail is by using a cousin domain, such as stackexcharge.com in place of stackexchange.com. A more sophisticated example: stackexchаnge.com uses an IDN homograph: a Cyrillic
а (can you tell which one I changed?). With custom domains under an attacker's control, they can set up and then pass their own SPF, DKIM, and DMARC. (In the real world, most cousin domains in phishing and BEC attacks use combosquatting, where extra words are added, like stackexchange-mail.com.)
I'd advocate for DKIM over SPF any day. It is indeed safer, though this mostly comes from the fact that SPF blesses entire hosts, and some hosts (like those of marketing partners) send mail for more than just the domain in question. If a sender can actually ensure they only send with valid and aligned DKIM, I'd recommend an SPF record of
v=spf1 ?all (which says nothing passes SPF, though nothing fails it either), but this is likely too difficult for most deployments. Certainly do not consider that without a very thorough DMARC feedback loop to ensure you know what it'll do and you know when a problem arises.